[前][次][番号順一覧][スレッド一覧]

ruby-changes:71949

From: Samuel <ko1@a...>
Date: Wed, 25 May 2022 12:24:47 +0900 (JST)
Subject: [ruby-changes:71949] 901525b107 (master): Add support for address sanitizer for amd64 and arm64.

https://git.ruby-lang.org/ruby.git/commit/?id=901525b107

From 901525b1079ac02da0122a76d8e4c3546a7f80f6 Mon Sep 17 00:00:00 2001
From: Samuel Williams <samuel.williams@o...>
Date: Thu, 19 May 2022 23:03:49 +1200
Subject: Add support for address sanitizer for amd64 and arm64.

---
 cont.c                    |  42 ++++++++++++++++-
 coroutine/amd64/Context.h |  25 ++++++++++
 coroutine/arm64/Context.h |  25 ++++++++++
 doc/hacking.md            | 115 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 205 insertions(+), 2 deletions(-)
 create mode 100644 doc/hacking.md

diff --git a/cont.c b/cont.c
index ee7b856bb1..713f8cb952 100644
--- a/cont.c
+++ b/cont.c
@@ -274,7 +274,6 @@ static ID fiber_initialize_keywords[2] = {0}; https://github.com/ruby/ruby/blob/trunk/cont.c#L274
 #define ERRNOMSG strerror(errno)
 
 // Locates the stack vacancy details for the given stack.
-// Requires that fiber_pool_vacancy fits within one page.
 inline static struct fiber_pool_vacancy *
 fiber_pool_vacancy_pointer(void * base, size_t size)
 {
@@ -285,6 +284,24 @@ fiber_pool_vacancy_pointer(void * base, size_t size) https://github.com/ruby/ruby/blob/trunk/cont.c#L284
     );
 }
 
+#if defined(COROUTINE_SANITIZE_ADDRESS)
+// Compute the base pointer for a vacant stack, for the area which can be poisoned.
+inline static void *
+fiber_pool_stack_poison_base(struct fiber_pool_stack * stack)
+{
+    STACK_GROW_DIR_DETECTION;
+
+    return (char*)stack->base + STACK_DIR_UPPER(RB_PAGE_SIZE, 0);
+}
+
+// Compute the size of the vacant stack, for the area that can be poisoned.
+inline static size_t
+fiber_pool_stack_poison_size(struct fiber_pool_stack * stack)
+{
+    return stack->size - RB_PAGE_SIZE;
+}
+#endif
+
 // Reset the current stack pointer and available size of the given stack.
 inline static void
 fiber_pool_stack_reset(struct fiber_pool_stack * stack)
@@ -634,6 +651,10 @@ fiber_pool_stack_acquire(struct fiber_pool * fiber_pool) https://github.com/ruby/ruby/blob/trunk/cont.c#L651
     VM_ASSERT(vacancy);
     VM_ASSERT(vacancy->stack.base);
 
+#if defined(COROUTINE_SANITIZE_ADDRESS)
+    __asan_unpoison_memory_region(fiber_pool_stack_poison_base(&vacancy->stack), fiber_pool_stack_poison_size(&vacancy->stack));
+#endif
+
     // Take the top item from the free list:
     fiber_pool->used += 1;
 
@@ -679,6 +700,10 @@ fiber_pool_stack_free(struct fiber_pool_stack * stack) https://github.com/ruby/ruby/blob/trunk/cont.c#L700
     // Not available in all versions of Windows.
     //DiscardVirtualMemory(base, size);
 #endif
+
+#if defined(COROUTINE_SANITIZE_ADDRESS)
+    __asan_poison_memory_region(fiber_pool_stack_poison_base(stack), fiber_pool_stack_poison_size(stack));
+#endif
 }
 
 // Release and return a stack to the vacancy list.
@@ -698,7 +723,7 @@ fiber_pool_stack_release(struct fiber_pool_stack * stack) https://github.com/ruby/ruby/blob/trunk/cont.c#L723
     fiber_pool_vacancy_reset(vacancy);
 
     // Push the vacancy into the vancancies list:
-    pool->vacancies = fiber_pool_vacancy_push(vacancy, stack->pool->vacancies);
+    pool->vacancies = fiber_pool_vacancy_push(vacancy, pool->vacancies);
     pool->used -= 1;
 
 #ifdef FIBER_POOL_ALLOCATION_FREE
@@ -751,6 +776,11 @@ static COROUTINE https://github.com/ruby/ruby/blob/trunk/cont.c#L776
 fiber_entry(struct coroutine_context * from, struct coroutine_context * to)
 {
     rb_fiber_t *fiber = to->argument;
+
+#if defined(COROUTINE_SANITIZE_ADDRESS)
+    __sanitizer_finish_switch_fiber(to->fake_stack, NULL, NULL);
+#endif
+
     rb_thread_t *thread = fiber->cont.saved_ec.thread_ptr;
 
 #ifdef COROUTINE_PTHREAD_CONTEXT
@@ -1379,9 +1409,17 @@ fiber_setcontext(rb_fiber_t *new_fiber, rb_fiber_t *old_fiber) https://github.com/ruby/ruby/blob/trunk/cont.c#L1409
 
     // if (DEBUG) fprintf(stderr, "fiber_setcontext: %p[%p] -> %p[%p]\n", (void*)old_fiber, old_fiber->stack.base, (void*)new_fiber, new_fiber->stack.base);
 
+#if defined(COROUTINE_SANITIZE_ADDRESS)
+    __sanitizer_start_switch_fiber(FIBER_TERMINATED_P(old_fiber) ? NULL : &old_fiber->context.fake_stack, new_fiber->context.stack_base, new_fiber->context.stack_size);
+#endif
+
     /* swap machine context */
     struct coroutine_context * from = coroutine_transfer(&old_fiber->context, &new_fiber->context);
 
+#if defined(COROUTINE_SANITIZE_ADDRESS)
+    __sanitizer_finish_switch_fiber(old_fiber->context.fake_stack, NULL, NULL);
+#endif
+
     if (from == NULL) {
         rb_syserr_fail(errno, "coroutine_transfer");
     }
diff --git a/coroutine/amd64/Context.h b/coroutine/amd64/Context.h
index f626a47225..44daa4e01a 100644
--- a/coroutine/amd64/Context.h
+++ b/coroutine/amd64/Context.h
@@ -19,10 +19,29 @@ https://github.com/ruby/ruby/blob/trunk/coroutine/amd64/Context.h#L19
 
 enum {COROUTINE_REGISTERS = 6};
 
+#if defined(__SANITIZE_ADDRESS__)
+    #define COROUTINE_SANITIZE_ADDRESS
+#elif defined(__has_feature)
+    #if __has_feature(address_sanitizer)
+        #define COROUTINE_SANITIZE_ADDRESS
+    #endif
+#endif
+
+#if defined(COROUTINE_SANITIZE_ADDRESS)
+#include <sanitizer/common_interface_defs.h>
+#include <sanitizer/asan_interface.h>
+#endif
+
 struct coroutine_context
 {
     void **stack_pointer;
     void *argument;
+
+#if defined(COROUTINE_SANITIZE_ADDRESS)
+    void *fake_stack;
+    void *stack_base;
+    size_t stack_size;
+#endif
 };
 
 typedef COROUTINE(* coroutine_start)(struct coroutine_context *from, struct coroutine_context *self);
@@ -39,6 +58,12 @@ static inline void coroutine_initialize( https://github.com/ruby/ruby/blob/trunk/coroutine/amd64/Context.h#L58
 ) {
     assert(start && stack && size >= 1024);
 
+#if defined(COROUTINE_SANITIZE_ADDRESS)
+    context->fake_stack = NULL;
+    context->stack_base = stack;
+    context->stack_size = size;
+#endif
+
     // Stack grows down. Force 16-byte alignment.
     char * top = (char*)stack + size;
     context->stack_pointer = (void**)((uintptr_t)top & ~0xF);
diff --git a/coroutine/arm64/Context.h b/coroutine/arm64/Context.h
index dbc6ac94fb..1472621f48 100644
--- a/coroutine/arm64/Context.h
+++ b/coroutine/arm64/Context.h
@@ -19,10 +19,29 @@ https://github.com/ruby/ruby/blob/trunk/coroutine/arm64/Context.h#L19
 
 enum {COROUTINE_REGISTERS = 0xb0 / 8};
 
+#if defined(__SANITIZE_ADDRESS__)
+    #define COROUTINE_SANITIZE_ADDRESS
+#elif defined(__has_feature)
+    #if __has_feature(address_sanitizer)
+        #define COROUTINE_SANITIZE_ADDRESS
+    #endif
+#endif
+
+#if defined(COROUTINE_SANITIZE_ADDRESS)
+#include <sanitizer/common_interface_defs.h>
+#include <sanitizer/asan_interface.h>
+#endif
+
 struct coroutine_context
 {
     void **stack_pointer;
     void *argument;
+
+#if defined(COROUTINE_SANITIZE_ADDRESS)
+    void *fake_stack;
+    void *stack_base;
+    size_t stack_size;
+#endif
 };
 
 typedef COROUTINE(* coroutine_start)(struct coroutine_context *from, struct coroutine_context *self);
@@ -39,6 +58,12 @@ static inline void coroutine_initialize( https://github.com/ruby/ruby/blob/trunk/coroutine/arm64/Context.h#L58
 ) {
     assert(start && stack && size >= 1024);
 
+#if defined(COROUTINE_SANITIZE_ADDRESS)
+    context->fake_stack = NULL;
+    context->stack_base = stack;
+    context->stack_size = size;
+#endif
+
     // Stack grows down. Force 16-byte alignment.
     char * top = (char*)stack + size;
     context->stack_pointer = (void**)((uintptr_t)top & ~0xF);
diff --git a/doc/hacking.md b/doc/hacking.md
new file mode 100644
index 0000000000..a9fa820e7e
--- /dev/null
+++ b/doc/hacking.md
@@ -0,0 +1,115 @@ https://github.com/ruby/ruby/blob/trunk/doc/hacking.md#L1
+# Ruby Hacking Guide
+
+This document gives  some helpful instructions which  should make your
+experience as a Ruby core developer easier.
+
+## Setup
+
+### Make
+
+It's common to want to compile things as quickly as possible. Ensuring
+`make`  has the  right `--jobs`  flag will  ensure all  processors are
+utilized when building  software projects To do  this effectively, you
+can set `MAKEFLAGS` in your shell configuration/profile:
+
+``` shell
+# On macOS with Fish shell:
+export MAKEFLAGS="--jobs "(sysctl -n hw.ncpu)
+
+# On macOS with Bash/ZSH shell:
+export MAKEFLAGS="--jobs $(sysctl -n hw.ncpu)"
+
+# On Linux with Fish shell:
+export MAKEFLAGS="--jobs "(nproc)
+
+# On Linux with Bash/ZSH shell:
+export MAKEFLAGS="--jobs $(nproc)"
+```
+
+## Configure Ruby
+
+It's generally advisable to use a build directory.
+
+``` shell
+./autogen.sh
+mkdir build
+cd build
+../configure --prefix $HOME/.rubies/ruby-head
+make install
+```
+
+### Without Documentation
+
+If you  are frequently  building Ruby,  this will  reduce the  time it
+takes to `make install`.
+
+``` shell
+../configure --disable-install-doc
+```
+
+## Running Ruby
+
+### Run Local Test Script
+
+You can create  a file in the Ruby source  root called `test.rb`.  You
+can build `miniruby` and execute this script:
+
+``` shell
+make run
+```
+
+If  you want  more  of the  standard library,  you  can use  `runruby`
+instead of `run`.
+
+## Running Tests
+
+You can run the following tests at once:
+
+``` shell
+make check
+```
+
+### Run Bootstrap Tests
+
+There are  a set of tests  in `bootstraptest/` which cover  most basic
+features of the core Ruby language.
+
+``` shell
+make test
+```
+
+### Run Extensive Tests
+
+There  are extensive  tests in  `test/` which  cover a  wide range  of
+features of the Ruby core language.
+
+``` shell
+make test-all
+```
+
+You can run specific tests by specifying their path:
+
+``` shell
+make test-all TESTS=../test/fiber/test_io.rb
+```
+
+### Run Ruby Spec Suite Tests
+
+The [Ruby Spec Suite](https://github.com/ruby/spec/) is a test suite
+that aims to provide an executable description for the behavior of the
+language.
+
+``` shell
+make test-spec
+```
+
+### Building with Address Sanitizer
+
+Using the address sanitizer is a great way to detect memory issues.
+
+``` shell
+> ./autogen.sh
+> mkdir build && cd build
+> ../configure cppflags="-fsanitize=address -fno-omit-frame-pointer" optflags=-O1 LDFLAGS (... truncated)

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]