mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
Add support for address sanitizer for amd64 and arm64.
This commit is contained in:
parent
d020334e9e
commit
901525b107
Notes:
git
2022-05-25 12:24:51 +09:00
4 changed files with 205 additions and 2 deletions
42
cont.c
42
cont.c
|
@ -274,7 +274,6 @@ static ID fiber_initialize_keywords[2] = {0};
|
|||
#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)
|
|||
);
|
||||
}
|
||||
|
||||
#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)
|
|||
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)
|
|||
// 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)
|
|||
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
|
|||
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)
|
|||
|
||||
// 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");
|
||||
}
|
||||
|
|
|
@ -19,10 +19,29 @@
|
|||
|
||||
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(
|
|||
) {
|
||||
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);
|
||||
|
|
|
@ -19,10 +19,29 @@
|
|||
|
||||
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(
|
|||
) {
|
||||
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);
|
||||
|
|
115
doc/hacking.md
Normal file
115
doc/hacking.md
Normal file
|
@ -0,0 +1,115 @@
|
|||
# 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="-fsanitize=address -fno-omit-frame-pointer"
|
||||
>
|
||||
```
|
Loading…
Reference in a new issue