]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: iterate through inlinings when processing recover()
authorKeith Randall <khr@golang.org>
Tue, 1 Jul 2025 22:29:12 +0000 (15:29 -0700)
committerKeith Randall <khr@golang.org>
Fri, 25 Jul 2025 18:26:30 +0000 (11:26 -0700)
We care about the wrapper-ness of logical frames, not physical frames.

Fixes #73916
Fixes #73917
Fixex #73920

Change-Id: Ia17c8390e71e6c0e13e23dcbb7bc7273ef25da90
Reviewed-on: https://go-review.googlesource.com/c/go/+/685375
Reviewed-by: Keith Randall <khr@google.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
src/runtime/panic.go
test/fixedbugs/issue73916.go [new file with mode: 0644]
test/fixedbugs/issue73916b.go [new file with mode: 0644]
test/fixedbugs/issue73917.go [new file with mode: 0644]
test/fixedbugs/issue73920.go [new file with mode: 0644]

index 1e61c90aefe2c529aa529361045645beed089dbd..bf79b6518e88c39e6b75eeffd7945e98bfec876a 100644 (file)
@@ -1142,18 +1142,21 @@ func gorecover(_ uintptr) any {
                nonWrapperFrames := 0
        loop:
                for ; u.valid(); u.next() {
-                       switch u.frame.fn.funcID {
-                       case abi.FuncIDWrapper:
-                               continue
-                       case abi.FuncID_gopanic:
-                               if u.frame.fp == uintptr(p.gopanicFP) && nonWrapperFrames > 0 {
-                                       canRecover = true
-                               }
-                               break loop
-                       default:
-                               nonWrapperFrames++
-                               if nonWrapperFrames > 1 {
+                       for iu, f := newInlineUnwinder(u.frame.fn, u.symPC()); f.valid(); f = iu.next(f) {
+                               sf := iu.srcFunc(f)
+                               switch sf.funcID {
+                               case abi.FuncIDWrapper:
+                                       continue
+                               case abi.FuncID_gopanic:
+                                       if u.frame.fp == uintptr(p.gopanicFP) && nonWrapperFrames > 0 {
+                                               canRecover = true
+                                       }
                                        break loop
+                               default:
+                                       nonWrapperFrames++
+                                       if nonWrapperFrames > 1 {
+                                               break loop
+                                       }
                                }
                        }
                }
diff --git a/test/fixedbugs/issue73916.go b/test/fixedbugs/issue73916.go
new file mode 100644 (file)
index 0000000..6060c3f
--- /dev/null
@@ -0,0 +1,32 @@
+// run
+
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+func callRecover() {
+       if recover() != nil {
+               println("recovered")
+       }
+}
+
+func F(int) { callRecover() }
+
+func main() {
+       mustPanic(func() {
+               defer F(1)
+               panic("XXX")
+       })
+}
+
+func mustPanic(f func()) {
+       defer func() {
+               r := recover()
+               if r == nil {
+                       panic("didn't panic")
+               }
+       }()
+       f()
+}
diff --git a/test/fixedbugs/issue73916b.go b/test/fixedbugs/issue73916b.go
new file mode 100644 (file)
index 0000000..29393fa
--- /dev/null
@@ -0,0 +1,34 @@
+// run
+
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+func callRecover() {
+       func() {
+               if recover() != nil {
+                       println("recovered")
+               }
+       }()
+}
+
+func F() int { callRecover(); return 0 }
+
+func main() {
+       mustPanic(func() {
+               defer F()
+               panic("XXX")
+       })
+}
+
+func mustPanic(f func()) {
+       defer func() {
+               r := recover()
+               if r == nil {
+                       panic("didn't panic")
+               }
+       }()
+       f()
+}
diff --git a/test/fixedbugs/issue73917.go b/test/fixedbugs/issue73917.go
new file mode 100644 (file)
index 0000000..9c0330d
--- /dev/null
@@ -0,0 +1,40 @@
+// run
+
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+func callRecover() {
+       if recover() != nil {
+               println("recovered")
+       }
+}
+
+type T int
+
+func (*T) M() { callRecover() }
+
+type S struct{ *T } // has a wrapper S.M wrapping (*T.M)
+
+var p = S{new(T)}
+
+var fn = S.M // using a function pointer to force using the wrapper
+
+func main() {
+       mustPanic(func() {
+               defer fn(p)
+               panic("XXX")
+       })
+}
+
+func mustPanic(f func()) {
+       defer func() {
+               r := recover()
+               if r == nil {
+                       panic("didn't panic")
+               }
+       }()
+       f()
+}
diff --git a/test/fixedbugs/issue73920.go b/test/fixedbugs/issue73920.go
new file mode 100644 (file)
index 0000000..f0a711b
--- /dev/null
@@ -0,0 +1,40 @@
+// run
+
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+func callRecover() {
+       if recover() != nil {
+               println("recovered")
+       }
+}
+
+type T int
+
+func (*T) M() { callRecover() }
+
+type S struct{ *T } // has a wrapper (*S).M wrapping (*T.M)
+
+var p = &S{new(T)}
+
+var fn = (*S).M // using a function pointer to force using the wrapper
+
+func main() {
+       mustPanic(func() {
+               defer fn(p)
+               panic("XXX")
+       })
+}
+
+func mustPanic(f func()) {
+       defer func() {
+               r := recover()
+               if r == nil {
+                       panic("didn't panic")
+               }
+       }()
+       f()
+}