aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/runtime/stack_test.go33
-rw-r--r--src/runtime/traceback.go11
2 files changed, 42 insertions, 2 deletions
diff --git a/src/runtime/stack_test.go b/src/runtime/stack_test.go
index 81a637ccb3..5d674470c1 100644
--- a/src/runtime/stack_test.go
+++ b/src/runtime/stack_test.go
@@ -310,6 +310,39 @@ func testDeferPtrsPanic(c chan int, i int) {
useStackAndCall(i, func() { panic(1) })
}
+//go:noinline
+func testDeferLeafSigpanic1() {
+ // Cause a sigpanic to be injected in this frame.
+ //
+ // This function has to be declared before
+ // TestDeferLeafSigpanic so the runtime will crash if we think
+ // this function's continuation PC is in
+ // TestDeferLeafSigpanic.
+ *(*int)(nil) = 0
+}
+
+// TestDeferLeafSigpanic tests defer matching around leaf functions
+// that sigpanic. This is tricky because on LR machines the outer
+// function and the inner function have the same SP, but it's critical
+// that we match up the defer correctly to get the right liveness map.
+// See issue #25499.
+func TestDeferLeafSigpanic(t *testing.T) {
+ // Push a defer that will walk the stack.
+ defer func() {
+ if err := recover(); err == nil {
+ t.Fatal("expected panic from nil pointer")
+ }
+ GC()
+ }()
+ // Call a leaf function. We must set up the exact call stack:
+ //
+ // defering function -> leaf function -> sigpanic
+ //
+ // On LR machines, the leaf function will have the same SP as
+ // the SP pushed for the defer frame.
+ testDeferLeafSigpanic1()
+}
+
// TestPanicUseStack checks that a chain of Panic structs on the stack are
// updated correctly if the stack grows during the deferred execution that
// happens as a result of the panic.
diff --git a/src/runtime/traceback.go b/src/runtime/traceback.go
index 0fd7ef1987..cc5e01eb8b 100644
--- a/src/runtime/traceback.go
+++ b/src/runtime/traceback.go
@@ -302,7 +302,14 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
// returns; everything live at earlier deferprocs is still live at that one.
frame.continpc = frame.pc
if waspanic {
- if _defer != nil && _defer.sp == frame.sp {
+ // We match up defers with frames using the SP.
+ // However, if the function has an empty stack
+ // frame, then it's possible (on LR machines)
+ // for multiple call frames to have the same
+ // SP. But, since a function with no frame
+ // can't push a defer, the defer can't belong
+ // to that frame.
+ if _defer != nil && _defer.sp == frame.sp && frame.sp != frame.fp {
frame.continpc = _defer.pc
} else {
frame.continpc = 0
@@ -310,7 +317,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
}
// Unwind our local defer stack past this frame.
- for _defer != nil && (_defer.sp == frame.sp || _defer.sp == _NoArgs) {
+ for _defer != nil && ((_defer.sp == frame.sp && frame.sp != frame.fp) || _defer.sp == _NoArgs) {
_defer = _defer.link
}