]> Cypherpunks.ru repositories - gostls13.git/blob - src/runtime/testdata/testprogcgo/stackswitch.c
runtime: clear g0 stack bounds in dropm
[gostls13.git] / src / runtime / testdata / testprogcgo / stackswitch.c
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.
4
5 //go:build unix && !android && !openbsd
6
7 // Required for darwin ucontext.
8 #define _XOPEN_SOURCE
9 // Required for netbsd stack_t if _XOPEN_SOURCE is set.
10 #define _XOPEN_SOURCE_EXTENDED  1
11 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
12
13 #include <assert.h>
14 #include <pthread.h>
15 #include <stddef.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <ucontext.h>
19
20 // musl libc does not provide getcontext, etc. Skip the test there.
21 //
22 // musl libc doesn't provide any direct detection mechanism. So assume any
23 // non-glibc linux is using musl.
24 //
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__)
28 #define MUSL 1
29 #endif
30 #if defined(MUSL)
31 void callStackSwitchCallbackFromThread(void) {
32         printf("SKIP\n");
33         exit(0);
34 }
35 #else
36
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)
41
42 static ucontext_t uctx_save, uctx_switch;
43
44 extern void stackSwitchCallback(void);
45
46 char *stack2;
47
48 static void *stackSwitchThread(void *arg) {
49         // Simple test: callback works from the normal system stack.
50         stackSwitchCallback();
51
52         // Next, verify that switching stacks doesn't break callbacks.
53
54         char *stack1 = malloc(STACK_SIZE);
55         if (stack1 == NULL) {
56                 perror("malloc");
57                 exit(1);
58         }
59
60         // Allocate the second stack before freeing the first to ensure we don't get
61         // the same address from malloc.
62         //
63         // Will be freed in stackSwitchThread2.
64         stack2 = malloc(STACK_SIZE);
65         if (stack1 == NULL) {
66                 perror("malloc");
67                 exit(1);
68         }
69
70         if (getcontext(&uctx_switch) == -1) {
71                 perror("getcontext");
72                 exit(1);
73         }
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);
78
79         if (swapcontext(&uctx_save, &uctx_switch) == -1) {
80                 perror("swapcontext");
81                 exit(1);
82         }
83
84         if (getcontext(&uctx_switch) == -1) {
85                 perror("getcontext");
86                 exit(1);
87         }
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);
92
93         if (swapcontext(&uctx_save, &uctx_switch) == -1) {
94                 perror("swapcontext");
95                 exit(1);
96         }
97
98         free(stack1);
99
100         return NULL;
101 }
102
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
106         // new thread.
107
108         // N.B. since we used a custom stack with makecontext,
109         // callbackUpdateSystemStack had to guess the bounds. Its guess assumes
110         // a 32KiB stack.
111         char *prev_stack_lo = stack2 + STACK_SIZE - (32*1024);
112
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;
118
119         if (getcontext(&uctx_switch) == -1) {
120                 perror("getcontext");
121                 exit(1);
122         }
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);
127
128         if (swapcontext(&uctx_save, &uctx_switch) == -1) {
129                 perror("swapcontext");
130                 exit(1);
131         }
132
133         free(stack2);
134
135         return NULL;
136 }
137
138 void callStackSwitchCallbackFromThread(void) {
139         pthread_t thread;
140         assert(pthread_create(&thread, NULL, stackSwitchThread, NULL) == 0);
141         assert(pthread_join(thread, NULL) == 0);
142
143         assert(pthread_create(&thread, NULL, stackSwitchThread2, NULL) == 0);
144         assert(pthread_join(thread, NULL) == 0);
145 }
146
147 #endif