Skip to content

Commit 1af424c

Browse files
prattmicgopherbot
authored andcommitted
runtime: clear g0 stack bounds in dropm
After CL 527715, needm uses callbackUpdateSystemStack to set the stack bounds for g0 on an M from the extra M list. Since callbackUpdateSystemStack is also used for recursive cgocallback, it does nothing if the stack is already in bounds. Currently, the stack bounds in an extra M may contain stale bounds from a previous thread that used this M and then returned it to the extra list in dropm. Typically a new thread will not have an overlapping stack with an old thread, but because the old thread has exited there is a small chance that the C memory allocator will allocate the new thread's stack partially or fully overlapping with the old thread's stack. If this occurs, then callbackUpdateSystemStack will not update the stack bounds. If in addition, the overlap is partial such that SP on cgocallback is close to the recorded stack lower bound, then Go may quickly "overflow" the stack and crash with "morestack on g0". Fix this by clearing the stack bounds in dropm, which ensures that callbackUpdateSystemStack will unconditionally update the bounds in needm. For #62440. Change-Id: Ic9e2052c2090dd679ed716d1a23a86d66cbcada7 Reviewed-on: https://go-review.googlesource.com/c/go/+/537695 Reviewed-by: Cherry Mui <[email protected]> Run-TryBot: Michael Pratt <[email protected]> Auto-Submit: Michael Pratt <[email protected]> TryBot-Bypass: Michael Pratt <[email protected]>
1 parent 5fe2035 commit 1af424c

File tree

2 files changed

+50
-1
lines changed

2 files changed

+50
-1
lines changed

src/runtime/proc.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2187,6 +2187,14 @@ func dropm() {
21872187

21882188
setg(nil)
21892189

2190+
// Clear g0 stack bounds to ensure that needm always refreshes the
2191+
// bounds when reusing this M.
2192+
g0 := mp.g0
2193+
g0.stack.hi = 0
2194+
g0.stack.lo = 0
2195+
g0.stackguard0 = 0
2196+
g0.stackguard1 = 0
2197+
21902198
putExtraM(mp)
21912199

21922200
msigrestore(sigmask)

src/runtime/testdata/testprogcgo/stackswitch.c

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ static ucontext_t uctx_save, uctx_switch;
4343

4444
extern void stackSwitchCallback(void);
4545

46+
char *stack2;
47+
4648
static void *stackSwitchThread(void *arg) {
4749
// Simple test: callback works from the normal system stack.
4850
stackSwitchCallback();
@@ -57,7 +59,9 @@ static void *stackSwitchThread(void *arg) {
5759

5860
// Allocate the second stack before freeing the first to ensure we don't get
5961
// the same address from malloc.
60-
char *stack2 = malloc(STACK_SIZE);
62+
//
63+
// Will be freed in stackSwitchThread2.
64+
stack2 = malloc(STACK_SIZE);
6165
if (stack1 == NULL) {
6266
perror("malloc");
6367
exit(1);
@@ -92,6 +96,40 @@ static void *stackSwitchThread(void *arg) {
9296
}
9397

9498
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+
95133
free(stack2);
96134

97135
return NULL;
@@ -101,6 +139,9 @@ void callStackSwitchCallbackFromThread(void) {
101139
pthread_t thread;
102140
assert(pthread_create(&thread, NULL, stackSwitchThread, NULL) == 0);
103141
assert(pthread_join(thread, NULL) == 0);
142+
143+
assert(pthread_create(&thread, NULL, stackSwitchThread2, NULL) == 0);
144+
assert(pthread_join(thread, NULL) == 0);
104145
}
105146

106147
#endif

0 commit comments

Comments
 (0)