From a46b1ad3573ae64964ffd736dc79291b58190281 Mon Sep 17 00:00:00 2001 From: Michael Pratt Date: Mon, 4 Sep 2023 09:55:01 -0400 Subject: runtime: allow update of system stack bounds on callback from C thread Since CL 495855, Ms are cached for C threads calling into Go, including the stack bounds of the system stack. Some C libraries (e.g., coroutine libraries) do manual stack management and may change stacks between calls to Go on the same thread. Changing the stack if there is more Go up the stack would be problematic. But if the calls are completely independent there is no particular reason for Go to care about the changing stack boundary. Thus, this CL allows the stack bounds to change in such cases. The primary downside here (besides additional complexity) is that normal systems that do not manipulate the stack may not notice unintentional stack corruption as quickly as before. Note that callbackUpdateSystemStack is written to be usable for the initial setup in needm as well as updating the stack in cgocallbackg. Fixes #62440. For #62130. Change-Id: I7841b056acea1111bdae3b718345a3bd3961b4a8 Reviewed-on: https://go-review.googlesource.com/c/go/+/525455 Run-TryBot: Michael Pratt Reviewed-by: Ian Lance Taylor LUCI-TryBot-Result: Go LUCI Reviewed-by: Cherry Mui TryBot-Result: Gopher Robot --- src/runtime/testdata/testprogcgo/stackswitch.c | 81 ++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 src/runtime/testdata/testprogcgo/stackswitch.c (limited to 'src/runtime/testdata/testprogcgo/stackswitch.c') diff --git a/src/runtime/testdata/testprogcgo/stackswitch.c b/src/runtime/testdata/testprogcgo/stackswitch.c new file mode 100644 index 0000000000..9c0c583bf4 --- /dev/null +++ b/src/runtime/testdata/testprogcgo/stackswitch.c @@ -0,0 +1,81 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build unix && !openbsd + +#include +#include +#include +#include +#include +#include + +// Use a stack size larger than the 32kb estimate in +// runtime.callbackUpdateSystemStack. This ensures that a second stack +// allocation won't accidentally count as in bounds of the first stack +#define STACK_SIZE (64ull << 10) + +static ucontext_t uctx_save, uctx_switch; + +extern void stackSwitchCallback(void); + +static void *stackSwitchThread(void *arg) { + // Simple test: callback works from the normal system stack. + stackSwitchCallback(); + + // Next, verify that switching stacks doesn't break callbacks. + + char *stack1 = malloc(STACK_SIZE); + if (stack1 == NULL) { + perror("malloc"); + exit(1); + } + + // Allocate the second stack before freeing the first to ensure we don't get + // the same address from malloc. + char *stack2 = malloc(STACK_SIZE); + if (stack1 == NULL) { + perror("malloc"); + exit(1); + } + + if (getcontext(&uctx_switch) == -1) { + perror("getcontext"); + exit(1); + } + uctx_switch.uc_stack.ss_sp = stack1; + uctx_switch.uc_stack.ss_size = STACK_SIZE; + uctx_switch.uc_link = &uctx_save; + makecontext(&uctx_switch, stackSwitchCallback, 0); + + if (swapcontext(&uctx_save, &uctx_switch) == -1) { + perror("swapcontext"); + exit(1); + } + + if (getcontext(&uctx_switch) == -1) { + perror("getcontext"); + exit(1); + } + uctx_switch.uc_stack.ss_sp = stack2; + uctx_switch.uc_stack.ss_size = STACK_SIZE; + uctx_switch.uc_link = &uctx_save; + makecontext(&uctx_switch, stackSwitchCallback, 0); + + if (swapcontext(&uctx_save, &uctx_switch) == -1) { + perror("swapcontext"); + exit(1); + } + + free(stack1); + free(stack2); + + return NULL; +} + +void callStackSwitchCallbackFromThread(void) { + pthread_t thread; + assert(pthread_create(&thread, NULL, stackSwitchThread, NULL) == 0); + assert(pthread_join(thread, NULL) == 0); +} -- cgit v1.3