diff options
-rw-r--r-- | src/runtime/proc.go | 8 | ||||
-rw-r--r-- | src/runtime/testdata/testprogcgo/stackswitch.c | 43 |
2 files changed, 50 insertions, 1 deletions
diff --git a/src/runtime/proc.go b/src/runtime/proc.go index eaea523ab5..0425aede23 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -2186,6 +2186,14 @@ func dropm() { setg(nil) + // Clear g0 stack bounds to ensure that needm always refreshes the + // bounds when reusing this M. + g0 := mp.g0 + g0.stack.hi = 0 + g0.stack.lo = 0 + g0.stackguard0 = 0 + g0.stackguard1 = 0 + putExtraM(mp) msigrestore(sigmask) diff --git a/src/runtime/testdata/testprogcgo/stackswitch.c b/src/runtime/testdata/testprogcgo/stackswitch.c index 2f79cc28ed..3473d5bd57 100644 --- a/src/runtime/testdata/testprogcgo/stackswitch.c +++ b/src/runtime/testdata/testprogcgo/stackswitch.c @@ -43,6 +43,8 @@ static ucontext_t uctx_save, uctx_switch; extern void stackSwitchCallback(void); +char *stack2; + static void *stackSwitchThread(void *arg) { // Simple test: callback works from the normal system stack. stackSwitchCallback(); @@ -57,7 +59,9 @@ static void *stackSwitchThread(void *arg) { // Allocate the second stack before freeing the first to ensure we don't get // the same address from malloc. - char *stack2 = malloc(STACK_SIZE); + // + // Will be freed in stackSwitchThread2. + stack2 = malloc(STACK_SIZE); if (stack1 == NULL) { perror("malloc"); exit(1); @@ -92,6 +96,40 @@ static void *stackSwitchThread(void *arg) { } free(stack1); + + return NULL; +} + +static void *stackSwitchThread2(void *arg) { + // New thread. Use stack bounds that partially overlap the previous + // bounds. needm should refresh the stack bounds anyway since this is a + // new thread. + + // N.B. since we used a custom stack with makecontext, + // callbackUpdateSystemStack had to guess the bounds. Its guess assumes + // a 32KiB stack. + char *prev_stack_lo = stack2 + STACK_SIZE - (32*1024); + + // New SP is just barely in bounds, but if we don't update the bounds + // we'll almost certainly overflow. The SP that + // callbackUpdateSystemStack sees already has some data pushed, so it + // will be a bit below what we set here. Thus we include some slack. + char *new_stack_hi = prev_stack_lo + 128; + + if (getcontext(&uctx_switch) == -1) { + perror("getcontext"); + exit(1); + } + uctx_switch.uc_stack.ss_sp = new_stack_hi - (STACK_SIZE / 2); + uctx_switch.uc_stack.ss_size = STACK_SIZE / 2; + uctx_switch.uc_link = &uctx_save; + makecontext(&uctx_switch, stackSwitchCallback, 0); + + if (swapcontext(&uctx_save, &uctx_switch) == -1) { + perror("swapcontext"); + exit(1); + } + free(stack2); return NULL; @@ -101,6 +139,9 @@ void callStackSwitchCallbackFromThread(void) { pthread_t thread; assert(pthread_create(&thread, NULL, stackSwitchThread, NULL) == 0); assert(pthread_join(thread, NULL) == 0); + + assert(pthread_create(&thread, NULL, stackSwitchThread2, NULL) == 0); + assert(pthread_join(thread, NULL) == 0); } #endif |