mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
runtime: don't crash if signal delivered on g0 stack
Also, if we changed the gsignal stack to match the stack we are executing on, restore it when returning from the signal handler, for safety. Fixes #18255. Change-Id: Ic289b36e4e38a56f8a6d4b5d74f68121c242e81a Reviewed-on: https://go-review.googlesource.com/34239 Run-TryBot: Ian Lance Taylor <iant@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Bryan Mills <bcmills@google.com> Reviewed-by: David Crawshaw <crawshaw@golang.org>
This commit is contained in:
parent
265e547658
commit
fded5dbb2f
3 changed files with 131 additions and 18 deletions
|
|
@ -212,25 +212,43 @@ func sigtrampgo(sig uint32, info *siginfo, ctx unsafe.Pointer) {
|
|||
}
|
||||
|
||||
// If some non-Go code called sigaltstack, adjust.
|
||||
setStack := false
|
||||
var gsignalStack gsignalStack
|
||||
sp := uintptr(unsafe.Pointer(&sig))
|
||||
if sp < g.m.gsignal.stack.lo || sp >= g.m.gsignal.stack.hi {
|
||||
var st stackt
|
||||
sigaltstack(nil, &st)
|
||||
if st.ss_flags&_SS_DISABLE != 0 {
|
||||
setg(nil)
|
||||
needm(0)
|
||||
noSignalStack(sig)
|
||||
dropm()
|
||||
if sp >= g.m.g0.stack.lo && sp < g.m.g0.stack.hi {
|
||||
// The signal was delivered on the g0 stack.
|
||||
// This can happen when linked with C code
|
||||
// using the thread sanitizer, which collects
|
||||
// signals then delivers them itself by calling
|
||||
// the signal handler directly when C code,
|
||||
// including C code called via cgo, calls a
|
||||
// TSAN-intercepted function such as malloc.
|
||||
st := stackt{ss_size: g.m.g0.stack.hi - g.m.g0.stack.lo}
|
||||
setSignalstackSP(&st, g.m.g0.stack.lo)
|
||||
setGsignalStack(&st, &gsignalStack)
|
||||
g.m.gsignal.stktopsp = getcallersp(unsafe.Pointer(&sig))
|
||||
setStack = true
|
||||
} else {
|
||||
var st stackt
|
||||
sigaltstack(nil, &st)
|
||||
if st.ss_flags&_SS_DISABLE != 0 {
|
||||
setg(nil)
|
||||
needm(0)
|
||||
noSignalStack(sig)
|
||||
dropm()
|
||||
}
|
||||
stsp := uintptr(unsafe.Pointer(st.ss_sp))
|
||||
if sp < stsp || sp >= stsp+st.ss_size {
|
||||
setg(nil)
|
||||
needm(0)
|
||||
sigNotOnStack(sig)
|
||||
dropm()
|
||||
}
|
||||
setGsignalStack(&st, &gsignalStack)
|
||||
g.m.gsignal.stktopsp = getcallersp(unsafe.Pointer(&sig))
|
||||
setStack = true
|
||||
}
|
||||
stsp := uintptr(unsafe.Pointer(st.ss_sp))
|
||||
if sp < stsp || sp >= stsp+st.ss_size {
|
||||
setg(nil)
|
||||
needm(0)
|
||||
sigNotOnStack(sig)
|
||||
dropm()
|
||||
}
|
||||
setGsignalStack(&st)
|
||||
g.m.gsignal.stktopsp = getcallersp(unsafe.Pointer(&sig))
|
||||
}
|
||||
|
||||
setg(g.m.gsignal)
|
||||
|
|
@ -238,6 +256,9 @@ func sigtrampgo(sig uint32, info *siginfo, ctx unsafe.Pointer) {
|
|||
c.fixsigcode(sig)
|
||||
sighandler(sig, info, ctx, g)
|
||||
setg(g)
|
||||
if setStack {
|
||||
restoreGsignalStack(&gsignalStack)
|
||||
}
|
||||
}
|
||||
|
||||
// sigpanic turns a synchronous signal into a run-time panic.
|
||||
|
|
@ -585,7 +606,7 @@ func minitSignalStack() {
|
|||
signalstack(&_g_.m.gsignal.stack)
|
||||
_g_.m.newSigstack = true
|
||||
} else {
|
||||
setGsignalStack(&st)
|
||||
setGsignalStack(&st, nil)
|
||||
_g_.m.newSigstack = false
|
||||
}
|
||||
}
|
||||
|
|
@ -618,14 +639,32 @@ func unminitSignals() {
|
|||
}
|
||||
}
|
||||
|
||||
// gsignalStack saves the fields of the gsignal stack changed by
|
||||
// setGsignalStack.
|
||||
type gsignalStack struct {
|
||||
stack stack
|
||||
stackguard0 uintptr
|
||||
stackguard1 uintptr
|
||||
stackAlloc uintptr
|
||||
stktopsp uintptr
|
||||
}
|
||||
|
||||
// setGsignalStack sets the gsignal stack of the current m to an
|
||||
// alternate signal stack returned from the sigaltstack system call.
|
||||
// It saves the old values in *old for use by restoreGsignalStack.
|
||||
// This is used when handling a signal if non-Go code has set the
|
||||
// alternate signal stack.
|
||||
//go:nosplit
|
||||
//go:nowritebarrierrec
|
||||
func setGsignalStack(st *stackt) {
|
||||
func setGsignalStack(st *stackt, old *gsignalStack) {
|
||||
g := getg()
|
||||
if old != nil {
|
||||
old.stack = g.m.gsignal.stack
|
||||
old.stackguard0 = g.m.gsignal.stackguard0
|
||||
old.stackguard1 = g.m.gsignal.stackguard1
|
||||
old.stackAlloc = g.m.gsignal.stackAlloc
|
||||
old.stktopsp = g.m.gsignal.stktopsp
|
||||
}
|
||||
stsp := uintptr(unsafe.Pointer(st.ss_sp))
|
||||
g.m.gsignal.stack.lo = stsp
|
||||
g.m.gsignal.stack.hi = stsp + st.ss_size
|
||||
|
|
@ -634,6 +673,19 @@ func setGsignalStack(st *stackt) {
|
|||
g.m.gsignal.stackAlloc = st.ss_size
|
||||
}
|
||||
|
||||
// restoreGsignalStack restores the gsignal stack to the value it had
|
||||
// before entering the signal handler.
|
||||
//go:nosplit
|
||||
//go:nowritebarrierrec
|
||||
func restoreGsignalStack(st *gsignalStack) {
|
||||
gp := getg().m.gsignal
|
||||
gp.stack = st.stack
|
||||
gp.stackguard0 = st.stackguard0
|
||||
gp.stackguard1 = st.stackguard1
|
||||
gp.stackAlloc = st.stackAlloc
|
||||
gp.stktopsp = st.stktopsp
|
||||
}
|
||||
|
||||
// signalstack sets the current thread's alternate signal stack to s.
|
||||
//go:nosplit
|
||||
func signalstack(s *stack) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue