1 // Copyright 2023 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 //go:build unix && !android && !openbsd
7 // Required for darwin ucontext.
9 // Required for netbsd stack_t if _XOPEN_SOURCE is set.
10 #define _XOPEN_SOURCE_EXTENDED 1
11 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
20 // musl libc does not provide getcontext, etc. Skip the test there.
22 // musl libc doesn't provide any direct detection mechanism. So assume any
23 // non-glibc linux is using musl.
25 // Note that bionic does not provide getcontext either, but that is skipped via
26 // the android build tag.
27 #if defined(__linux__) && !defined(__GLIBC__)
31 void callStackSwitchCallbackFromThread(void) {
37 // Use a stack size larger than the 32kb estimate in
38 // runtime.callbackUpdateSystemStack. This ensures that a second stack
39 // allocation won't accidentally count as in bounds of the first stack
40 #define STACK_SIZE (64ull << 10)
42 static ucontext_t uctx_save, uctx_switch;
44 extern void stackSwitchCallback(void);
48 static void *stackSwitchThread(void *arg) {
49 // Simple test: callback works from the normal system stack.
50 stackSwitchCallback();
52 // Next, verify that switching stacks doesn't break callbacks.
54 char *stack1 = malloc(STACK_SIZE);
60 // Allocate the second stack before freeing the first to ensure we don't get
61 // the same address from malloc.
63 // Will be freed in stackSwitchThread2.
64 stack2 = malloc(STACK_SIZE);
70 if (getcontext(&uctx_switch) == -1) {
74 uctx_switch.uc_stack.ss_sp = stack1;
75 uctx_switch.uc_stack.ss_size = STACK_SIZE;
76 uctx_switch.uc_link = &uctx_save;
77 makecontext(&uctx_switch, stackSwitchCallback, 0);
79 if (swapcontext(&uctx_save, &uctx_switch) == -1) {
80 perror("swapcontext");
84 if (getcontext(&uctx_switch) == -1) {
88 uctx_switch.uc_stack.ss_sp = stack2;
89 uctx_switch.uc_stack.ss_size = STACK_SIZE;
90 uctx_switch.uc_link = &uctx_save;
91 makecontext(&uctx_switch, stackSwitchCallback, 0);
93 if (swapcontext(&uctx_save, &uctx_switch) == -1) {
94 perror("swapcontext");
103 static void *stackSwitchThread2(void *arg) {
104 // New thread. Use stack bounds that partially overlap the previous
105 // bounds. needm should refresh the stack bounds anyway since this is a
108 // N.B. since we used a custom stack with makecontext,
109 // callbackUpdateSystemStack had to guess the bounds. Its guess assumes
111 char *prev_stack_lo = stack2 + STACK_SIZE - (32*1024);
113 // New SP is just barely in bounds, but if we don't update the bounds
114 // we'll almost certainly overflow. The SP that
115 // callbackUpdateSystemStack sees already has some data pushed, so it
116 // will be a bit below what we set here. Thus we include some slack.
117 char *new_stack_hi = prev_stack_lo + 128;
119 if (getcontext(&uctx_switch) == -1) {
120 perror("getcontext");
123 uctx_switch.uc_stack.ss_sp = new_stack_hi - (STACK_SIZE / 2);
124 uctx_switch.uc_stack.ss_size = STACK_SIZE / 2;
125 uctx_switch.uc_link = &uctx_save;
126 makecontext(&uctx_switch, stackSwitchCallback, 0);
128 if (swapcontext(&uctx_save, &uctx_switch) == -1) {
129 perror("swapcontext");
138 void callStackSwitchCallbackFromThread(void) {
140 assert(pthread_create(&thread, NULL, stackSwitchThread, NULL) == 0);
141 assert(pthread_join(thread, NULL) == 0);
143 assert(pthread_create(&thread, NULL, stackSwitchThread2, NULL) == 0);
144 assert(pthread_join(thread, NULL) == 0);