]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: fix bp restoration in panic recovery for arm64
authorMichael Anthony Knyszek <mknyszek@google.com>
Mon, 28 Aug 2023 19:26:46 +0000 (19:26 +0000)
committerMichael Knyszek <mknyszek@google.com>
Mon, 28 Aug 2023 21:36:31 +0000 (21:36 +0000)
Previously, the frame pointer wouldn't be restored at all, which could
cause panics during frame pointer unwinding. As of CL 516157, the frame
pointer is restored, but it's restored incorrectly on arm64: on arm64,
the frame pointer points one word below SP, but here it's one below
panic.fp which is the stack pointer of the caller's frame (nothing to do
with the architectural bp).

For #61766.

Change-Id: I86504b85a4d741df5939b51c914d9e7c8d6edaad
Reviewed-on: https://go-review.googlesource.com/c/go/+/523697
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Run-TryBot: Michael Knyszek <mknyszek@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>

src/runtime/callers_test.go
src/runtime/panic.go

index 42091b04fca24c73f70e1a5f445760feabe7e75d..49a1d5a6f7612439311f668eaba072dd5a3263ed 100644 (file)
@@ -478,6 +478,7 @@ func TestFPUnwindAfterRecovery(t *testing.T) {
                        pcs[i] = 10
                }
                runtime.FPCallers(pcs)
+               t.Logf("%v", pcs)
        }()
        defer func() {
                if recover() == nil {
index acbbaa718f9ba3ff624228c0052878e02e5acf84..cb624ec9ef5651086edc1352f6553589693c34fd 100644 (file)
@@ -313,10 +313,10 @@ func deferproc(fn func()) {
 // The g._defer list is now a linked list of deferred calls,
 // but an atomic list hanging off:
 //
-//     g._defer => d4 -> d3 -> drangefunc -> d2 -> d1 -> nil
-//                              | .head
-//                              |
-//                              +--> dY -> dX -> nil
+//             g._defer => d4 -> d3 -> drangefunc -> d2 -> d1 -> nil
+//                                  | .head
+//                                  |
+//                                  +--> dY -> dX -> nil
 //
 // with each -> indicating a d.link pointer, and where drangefunc
 // has the d.rangefunc = true bit set.
@@ -340,10 +340,10 @@ func deferproc(fn func()) {
 //
 // That is, deferconvert changes this list:
 //
-//     g._defer => drangefunc -> d2 -> d1 -> nil
-//                  | .head
-//                  |
-//                  +--> dY -> dX -> nil
+//             g._defer => drangefunc -> d2 -> d1 -> nil
+//                      | .head
+//                      |
+//                      +--> dY -> dX -> nil
 //
 // into this list:
 //
@@ -1149,15 +1149,20 @@ func recovery(gp *g) {
        gp.sched.sp = sp
        gp.sched.pc = pc
        gp.sched.lr = 0
-       // fp points to the stack pointer at the caller, which is the top of the
-       // stack frame. The frame pointer used for unwinding is the word
-       // immediately below it.
-       gp.sched.bp = fp - goarch.PtrSize
-       if !usesLR {
+       // Restore the bp on platforms that support frame pointers.
+       // N.B. It's fine to not set anything for platforms that don't
+       // support frame pointers, since nothing consumes them.
+       switch {
+       case goarch.IsAmd64 != 0:
                // on x86, fp actually points one word higher than the top of
                // the frame since the return address is saved on the stack by
                // the caller
-               gp.sched.bp -= goarch.PtrSize
+               gp.sched.bp = fp - 2*goarch.PtrSize
+       case goarch.IsArm64 != 0:
+               // on arm64, the architectural bp points one word higher
+               // than the sp. fp is totally useless to us here, because it
+               // only gets us to the caller's fp.
+               gp.sched.bp = sp - goarch.PtrSize
        }
        gp.sched.ret = 1
        gogo(&gp.sched)