]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: don't grow the stack on sigpanic if throwsplit
authorAustin Clements <austin@google.com>
Fri, 12 Jan 2018 17:03:49 +0000 (12:03 -0500)
committerAustin Clements <austin@google.com>
Tue, 23 Jan 2018 19:50:18 +0000 (19:50 +0000)
Currently, if a _SigPanic signal arrives in a throwsplit context,
nothing is stopping the runtime from injecting a call to sigpanic that
may attempt to grow the stack. This will fail and, in turn, mask the
real problem.

Fix this by checking for throwsplit in the signal handler itself
before injecting the sigpanic call.

Updates #21431, where this problem is likely masking the real problem.

Change-Id: I64b61ff08e8c4d6f6c0fb01315d7d5e66bf1d3e2
Reviewed-on: https://go-review.googlesource.com/87595
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
src/runtime/os3_plan9.go
src/runtime/panic.go
src/runtime/signal_sighandler.go
src/runtime/signal_unix.go
src/runtime/signal_windows.go

index 3b65a2c9ba18422d495514260291ba6cd659f08f..0b313d75e3b9d37b31eff0257e9329708a5e6469 100644 (file)
@@ -45,6 +45,11 @@ func sighandler(_ureg *ureg, note *byte, gp *g) int {
                        break
                }
        }
+       if flags&_SigPanic != 0 && gp.throwsplit {
+               // We can't safely sigpanic because it may grow the
+               // stack. Abort in the signal handler instead.
+               flags = (flags &^ _SigPanic) | _SigThrow
+       }
        if flags&_SigGoExit != 0 {
                exits((*byte)(add(unsafe.Pointer(note), 9))) // Strip "go: exit " prefix.
        }
index 6fa99d6493480a726aec14d98ad8eb0a73d769fa..106ca5bffcd88baa181d435572bf1cd9f24bb4c9 100644 (file)
@@ -752,6 +752,9 @@ func dopanic_m(gp *g, pc, sp uintptr) {
        exit(2)
 }
 
+// canpanic returns false if a signal should throw instead of
+// panicking.
+//
 //go:nosplit
 func canpanic(gp *g) bool {
        // Note that g is m->gsignal, different from gp.
index f24a117fcdf566acd5d3d9f03fa96ca77cb75666..bf2237c9814c8205c94acce4793bad14d479f9f6 100644 (file)
@@ -38,6 +38,11 @@ func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) {
        if sig < uint32(len(sigtable)) {
                flags = sigtable[sig].flags
        }
+       if flags&_SigPanic != 0 && gp.throwsplit {
+               // We can't safely sigpanic because it may grow the
+               // stack. Abort in the signal handler instead.
+               flags = (flags &^ _SigPanic) | _SigThrow
+       }
        if c.sigcode() != _SI_USER && flags&_SigPanic != 0 {
                // The signal is going to cause a panic.
                // Arrange the stack so that it looks like the point
index ac191f302fac419b71c3dacf73fa10c14d53d98c..78649c52a9fbf312547ab8bbaa5b28b7c8ac9125 100644 (file)
@@ -360,6 +360,12 @@ func sigtrampgo(sig uint32, info *siginfo, ctx unsafe.Pointer) {
 // the signal handler. The effect is that the program will act as
 // though the function that got the signal simply called sigpanic
 // instead.
+//
+// This must NOT be nosplit because the linker doesn't know where
+// sigpanic calls can be injected.
+//
+// The signal handler must not inject a call to sigpanic if
+// getg().throwsplit, since sigpanic may need to grow the stack.
 func sigpanic() {
        g := getg()
        if !canpanic(g) {
index 7d230517f60df300051644b927d64e7ad3fc58e5..518aac3c48ab8fc963b14385682e550dab4ddd09 100644 (file)
@@ -71,6 +71,12 @@ func exceptionhandler(info *exceptionrecord, r *context, gp *g) int32 {
                return _EXCEPTION_CONTINUE_SEARCH
        }
 
+       if gp.throwsplit {
+               // We can't safely sigpanic because it may grow the
+               // stack. Let it fall through.
+               return _EXCEPTION_CONTINUE_SEARCH
+       }
+
        // Make it look like a call to the signal func.
        // Have to pass arguments out of band since
        // augmenting the stack frame would break