Make stack bounds detection work with ASAN

Where a local variable is used as part of the stack bounds detection, it
has to actually be on the stack. ASAN can put local variable on "fake
stacks", however, with addresses in different memory mappings. This
completely destroys the stack bounds calculation, and can lead to e.g.
things not getting GC marked on the machine stack or stackoverflow
checks that always fail.

The __asan_addr_is_in_fake_stack helper can be used to get the _real_
stack address of such variables, and thus perform the stack size
calculation properly

[Bug #20001]
This commit is contained in:
KJ Tsanaktsidis 2023-11-12 13:34:43 +11:00
Родитель 807714447e
Коммит cabdaebc70
3 изменённых файлов: 38 добавлений и 6 удалений

Просмотреть файл

@ -64,6 +64,8 @@
# define __asan_poison_memory_region(x, y)
# define __asan_unpoison_memory_region(x, y)
# define __asan_region_is_poisoned(x, y) 0
# define __asan_get_current_fake_stack() NULL
# define __asan_addr_is_in_fake_stack(fake_stack, slot, start, end) NULL
#endif
#if !__has_feature(memory_sanitizer)
@ -183,4 +185,28 @@ asan_unpoison_object(VALUE obj, bool newobj_p)
asan_unpoison_memory_region(ptr, SIZEOF_VALUE, newobj_p);
}
/*!
* Checks if the given pointer is on an ASAN fake stack. If so, it returns the
* address this variable has on the real frame; if not, it returns the origin
* address unmodified.
*
* n.b. - _dereferencing_ the returned address is meaningless and should not
* be done; even though ASAN reserves space for the variable in both the real and
* fake stacks, the _value_ of that variable is only in the fake stack.
*
* n.b. - this only works for addresses passed in from local variables on the same
* thread, because the ASAN fake stacks are threadlocal.
*
* \param[in] slot the address of some local variable
* \retval a pointer to something from that frame on the _real_ machine stack
*/
static inline void *
asan_get_real_stack_addr(void* slot)
{
VALUE *addr;
addr = __asan_addr_is_in_fake_stack(__asan_get_current_fake_stack(), slot, NULL, NULL);
return addr ? addr : slot;
}
#endif /* INTERNAL_SANITIZERS_H */

Просмотреть файл

@ -12,6 +12,7 @@
#ifdef THREAD_SYSTEM_DEPENDENT_IMPLEMENTATION
#include "internal/gc.h"
#include "internal/sanitizers.h"
#include "rjit.h"
#ifdef HAVE_SYS_RESOURCE_H
@ -1966,6 +1967,9 @@ static void
native_thread_init_main_thread_stack(void *addr)
{
native_main_thread.id = pthread_self();
#ifdef RUBY_ASAN_ENABLED
addr = asan_get_real_stack_addr((void *)addr);
#endif
#if MAINSTACKADDR_AVAILABLE
if (native_main_thread.stack_maxsize) return;
@ -2051,6 +2055,9 @@ static int
native_thread_init_stack(rb_thread_t *th, void *local_in_parent_frame)
{
rb_nativethread_id_t curr = pthread_self();
#ifdef RUBY_ASAN_ENABLED
local_in_parent_frame = asan_get_real_stack_addr(local_in_parent_frame);
#endif
if (!native_main_thread.id) {
/* This thread is the first thread, must be the main thread -
@ -2070,7 +2077,7 @@ native_thread_init_stack(rb_thread_t *th, void *local_in_parent_frame)
if (get_stack(&start, &size) == 0) {
uintptr_t diff = (uintptr_t)start - (uintptr_t)local_in_parent_frame;
th->ec->machine.stack_start = (uintptr_t)local_in_parent_frame;
th->ec->machine.stack_start = local_in_parent_frame;
th->ec->machine.stack_maxsize = size - diff;
}
}
@ -2197,12 +2204,10 @@ call_thread_start_func_2(rb_thread_t *th)
on a new thread, and replacing that data on fiber-switch would break it (see
bug #13887) */
VALUE stack_start = 0;
VALUE *stack_start_addr = &stack_start;
VALUE *stack_start_addr = asan_get_real_stack_addr(&stack_start);
native_thread_init_stack(th, stack_start_addr);
thread_start_func_2(th, th->ec->machine.stack_start);
/* Ensure that stack_start really was spilled to the stack */
RB_GC_GUARD(stack_start)
}
static void *

Просмотреть файл

@ -11,6 +11,7 @@
#ifdef THREAD_SYSTEM_DEPENDENT_IMPLEMENTATION
#include "internal/sanitizers.h"
#include <process.h>
#define TIME_QUANTUM_USEC (10 * 1000)
@ -592,7 +593,7 @@ COMPILER_WARNING_IGNORED(-Wmaybe-uninitialized)
static inline SIZE_T
query_memory_basic_info(PMEMORY_BASIC_INFORMATION mi, void *local_in_parent_frame)
{
return VirtualQuery(local_in_parent_frame, mi, sizeof(*mi));
return VirtualQuery(asan_get_real_stack_addr(local_in_parent_frame), mi, sizeof(*mi));
}
COMPILER_WARNING_POP