Pass down "stack start" variables from closer to the top of the stack

The implementation of `native_thread_init_stack` for the various
threading models can use the address of a local variable as part of the
calculation of the machine stack extents:

* pthreads uses it as a lower-bound on the start of the stack, because
  glibc (and maybe other libcs) can store its own data on the stack
  before calling into user code on thread creation.
* win32 uses it as an argument to VirtualQuery, which gets the extent of
  the memory mapping which contains the variable

However, the local being used for this is actually allocated _inside_
the `native_thread_init_stack` frame; that means the caller might
allocate a VALUE on the stack that actually lies outside the bounds
stored in machine.stack_{start,end}.

A local variable from one level above the topmost frame that stores
VALUEs on the stack must be drilled down into the call to
`native_thread_init_stack` to be used in the calculation. This probably
doesn't _really_ matter for the win32 case (they'll be in the same
memory mapping so VirtualQuery should return the same thing), but
definitely could matter for the pthreads case.

[Bug #20001]
This commit is contained in:
KJ Tsanaktsidis 2023-11-12 13:24:55 +11:00
parent 6a45320c25
commit 4ba8f0dc99
9 changed files with 33 additions and 22 deletions

View file

@ -1964,7 +1964,7 @@ reserve_stack(volatile char *limit, size_t size)
#undef ruby_init_stack
void
ruby_init_stack(volatile VALUE *addr)
ruby_init_stack(volatile void *addr)
{
native_main_thread.id = pthread_self();
@ -2049,7 +2049,7 @@ ruby_init_stack(volatile VALUE *addr)
{int err = (expr); if (err) {rb_bug_errno(#expr, err);}}
static int
native_thread_init_stack(rb_thread_t *th)
native_thread_init_stack(rb_thread_t *th, void *local_in_parent_frame)
{
rb_nativethread_id_t curr = pthread_self();
@ -2064,8 +2064,8 @@ native_thread_init_stack(rb_thread_t *th)
size_t size;
if (get_stack(&start, &size) == 0) {
uintptr_t diff = (uintptr_t)start - (uintptr_t)&curr;
th->ec->machine.stack_start = (VALUE *)&curr;
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_maxsize = size - diff;
}
}
@ -2185,8 +2185,19 @@ native_thread_create_dedicated(rb_thread_t *th)
static void
call_thread_start_func_2(rb_thread_t *th)
{
native_thread_init_stack(th);
/* Capture the address of a local in this stack frame to mark the beginning of the
machine stack for this thread. This is required even if we can tell the real
stack beginning from the pthread API in native_thread_init_stack, because
glibc stores some of its own data on the stack before calling into user code
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;
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 *