diff --git a/src/runtime/callers_test.go b/src/runtime/callers_test.go index 3cd1b40ec9..302e33deeb 100644 --- a/src/runtime/callers_test.go +++ b/src/runtime/callers_test.go @@ -254,9 +254,8 @@ func TestCallersDivZeroPanic(t *testing.T) { func TestCallersDeferNilFuncPanic(t *testing.T) { // Make sure we don't have any extra frames on the stack. We cut off the check // at runtime.sigpanic, because non-open-coded defers (which may be used in - // non-opt or race checker mode) include an extra 'jmpdefer' frame (which is - // where the nil pointer deref happens). We could consider hiding jmpdefer in - // tracebacks. + // non-opt or race checker mode) include an extra 'deferreturn' frame (which is + // where the nil pointer deref happens). state := 1 want := []string{"runtime.Callers", "runtime_test.TestCallersDeferNilFuncPanic.func1", "runtime.gopanic", "runtime.panicmem", "runtime.sigpanic"} @@ -279,3 +278,32 @@ func TestCallersDeferNilFuncPanic(t *testing.T) { // function exit, rather than at the defer statement. state = 2 } + +// Same test, but forcing non-open-coded defer by putting the defer in a loop. See +// issue #36050 +func TestCallersDeferNilFuncPanicWithLoop(t *testing.T) { + state := 1 + want := []string{"runtime.Callers", "runtime_test.TestCallersDeferNilFuncPanicWithLoop.func1", + "runtime.gopanic", "runtime.panicmem", "runtime.sigpanic", "runtime.deferreturn", "runtime_test.TestCallersDeferNilFuncPanicWithLoop"} + + defer func() { + if r := recover(); r == nil { + t.Fatal("did not panic") + } + pcs := make([]uintptr, 20) + pcs = pcs[:runtime.Callers(0, pcs)] + testCallersEqual(t, pcs, want) + if state == 1 { + t.Fatal("nil defer func panicked at defer time rather than function exit time") + } + + }() + + for i := 0; i < 1; i++ { + var f func() + defer f() + } + // Use the value of 'state' to make sure nil defer func f causes panic at + // function exit, rather than at the defer statement. + state = 2 +} diff --git a/src/runtime/panic.go b/src/runtime/panic.go index 0823f11e98..4cb6c8a360 100644 --- a/src/runtime/panic.go +++ b/src/runtime/panic.go @@ -561,6 +561,12 @@ func deferreturn(arg0 uintptr) { d.fn = nil gp._defer = d.link freedefer(d) + // If the defer function pointer is nil, force the seg fault to happen + // here rather than in jmpdefer. gentraceback() throws an error if it is + // called with a callback on an LR architecture and jmpdefer is on the + // stack, because the stack trace can be incorrect in that case - see + // issue #8153). + _ = fn.fn jmpdefer(fn, uintptr(unsafe.Pointer(&arg0))) }