Skip to content

Commit 3a778ff

Browse files
committed
runtime: check for g0 stack last in signal handler
In the signal handler, we adjust gsingal's stack to the stack where the signal is delivered. TSAN may deliver signals to the g0 stack, so we have a special case for the g0 stack. However, we don't have very good accuracy in determining the g0 stack's bounds, as it is system allocated and we don't know where it is exactly. If g0.stack.lo is too low, the condition may be triggered incorrectly, where we thought the signal is delivered to the g0 stack but it is actually not. In this case, as the stack bounds is actually wrong, when the stack grows, it may go below the (inaccurate) lower bound, causing "morestack on gsignal" crash. Check for g0 stack last to avoid this situation. There could still be false positives, but for those cases we'll crash either way. (If we could in some way determine the g0 stack bounds accurately, this would not matter (but probably doesn't hurt).) Fixes #43853. Change-Id: I759717c5aa2b0deb83ffb23e57b7625a6b249ee8 Reviewed-on: https://go-review.googlesource.com/c/go/+/285772 Trust: Cherry Zhang <[email protected]> Reviewed-by: Michael Pratt <[email protected]>
1 parent a2cef9b commit 3a778ff

File tree

2 files changed

+22
-13
lines changed

2 files changed

+22
-13
lines changed

src/runtime/proc.go

+5
Original file line numberDiff line numberDiff line change
@@ -1251,6 +1251,11 @@ func mstart() {
12511251
// Initialize stack bounds from system stack.
12521252
// Cgo may have left stack size in stack.hi.
12531253
// minit may update the stack bounds.
1254+
//
1255+
// Note: these bounds may not be very accurate.
1256+
// We set hi to &size, but there are things above
1257+
// it. The 1024 is supposed to compensate this,
1258+
// but is somewhat arbitrary.
12541259
size := _g_.stack.hi
12551260
if size == 0 {
12561261
size = 8192 * sys.StackGuardMultiplier

src/runtime/signal_unix.go

+17-13
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,14 @@ func adjustSignalStack(sig uint32, mp *m, gsigStack *gsignalStack) bool {
475475
return false
476476
}
477477

478+
var st stackt
479+
sigaltstack(nil, &st)
480+
stsp := uintptr(unsafe.Pointer(st.ss_sp))
481+
if st.ss_flags&_SS_DISABLE == 0 && sp >= stsp && sp < stsp+st.ss_size {
482+
setGsignalStack(&st, gsigStack)
483+
return true
484+
}
485+
478486
if sp >= mp.g0.stack.lo && sp < mp.g0.stack.hi {
479487
// The signal was delivered on the g0 stack.
480488
// This can happen when linked with C code
@@ -483,29 +491,25 @@ func adjustSignalStack(sig uint32, mp *m, gsigStack *gsignalStack) bool {
483491
// the signal handler directly when C code,
484492
// including C code called via cgo, calls a
485493
// TSAN-intercepted function such as malloc.
494+
//
495+
// We check this condition last as g0.stack.lo
496+
// may be not very accurate (see mstart).
486497
st := stackt{ss_size: mp.g0.stack.hi - mp.g0.stack.lo}
487498
setSignalstackSP(&st, mp.g0.stack.lo)
488499
setGsignalStack(&st, gsigStack)
489500
return true
490501
}
491502

492-
var st stackt
493-
sigaltstack(nil, &st)
503+
// sp is not within gsignal stack, g0 stack, or sigaltstack. Bad.
504+
setg(nil)
505+
needm()
494506
if st.ss_flags&_SS_DISABLE != 0 {
495-
setg(nil)
496-
needm()
497507
noSignalStack(sig)
498-
dropm()
499-
}
500-
stsp := uintptr(unsafe.Pointer(st.ss_sp))
501-
if sp < stsp || sp >= stsp+st.ss_size {
502-
setg(nil)
503-
needm()
508+
} else {
504509
sigNotOnStack(sig)
505-
dropm()
506510
}
507-
setGsignalStack(&st, gsigStack)
508-
return true
511+
dropm()
512+
return false
509513
}
510514

511515
// crashing is the number of m's we have waited for when implementing

0 commit comments

Comments
 (0)