diff options
author | KJ Tsanaktsidis <kj@kjtsanaktsidis.id.au> | 2023-11-12 13:24:55 +1100 |
---|---|---|
committer | KJ Tsanaktsidis <kj@kjtsanaktsidis.id.au> | 2024-01-12 17:29:48 +1100 |
commit | 4ba8f0dc993953d3ddda6328e3ef17a2fc2cbde5 (patch) | |
tree | 1f8045f587259f556974f3001d6a16bdf06b4fd1 /thread_pthread.c | |
parent | 6a45320c256f25e9fcdf9d969a45b85c885e28f2 (diff) |
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]
Diffstat (limited to 'thread_pthread.c')
-rw-r--r-- | thread_pthread.c | 21 |
1 files changed, 16 insertions, 5 deletions
diff --git a/thread_pthread.c b/thread_pthread.c index 6d2f55a957..a6a6c9d127 100644 --- a/thread_pthread.c +++ b/thread_pthread.c @@ -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 * |