aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCherry Mui <cherryyz@google.com>2022-04-15 12:23:06 -0400
committerHeschi Kreinick <heschi@google.com>2022-07-22 17:25:27 +0000
commit12e00f6c6b5f37eb8b268a8eaa64d80b7f5b6dcc (patch)
treef5b08616c19ebb35d974e943e6212900f44a6d87
parent88a06f40dfcdc4d37346be169f2b1b9070f38bb3 (diff)
downloadgo-12e00f6c6b5f37eb8b268a8eaa64d80b7f5b6dcc.tar.gz
go-12e00f6c6b5f37eb8b268a8eaa64d80b7f5b6dcc.zip
[release-branch.go1.18] runtime: use saved LR when unwinding through morestack
On LR machine, consider F calling G calling H, which grows stack. The stack looks like ... G's frame: ... locals ... saved LR = return PC in F <- SP points here at morestack H's frame (to be created) At morestack, we save gp.sched.pc = H's morestack call gp.sched.sp = H's entry SP (the arrow above) gp.sched.lr = return PC in G Currently, when unwinding through morestack (if _TraceJumpStack is set), we switch PC and SP but not LR. We then have frame.pc = H's morestack call frame.sp = H's entry SP (the arrow above) As LR is not set, we load it from stack at *sp, so frame.lr = return PC in F As the SP hasn't decremented at the morestack call, frame.fp = frame.sp = H's entry SP Unwinding a frame, we have frame.pc = old frame.lr = return PC in F frame.sp = old frame.fp = H's entry SP a.k.a. G's SP The PC and SP don't match. The unwinding will go off if F and G have different frame sizes. Fix this by preserving the LR when switching stack. Also add code to detect infinite loop in unwinding. TODO: add some test. I can reproduce the infinite loop (or throw with added check) but the frequency is low. Fixes #53112. Updates #52116. Change-Id: I6e1294f1c6e55f664c962767a1cf6c466a0c0eff Reviewed-on: https://go-review.googlesource.com/c/go/+/400575 TryBot-Result: Gopher Robot <gobot@golang.org> Run-TryBot: Cherry Mui <cherryyz@google.com> Reviewed-by: Eric Fang <eric.fang@arm.com> Reviewed-by: Benny Siegert <bsiegert@gmail.com> (cherry picked from commit 74f00094220f26c80fbaab6eca28c3a664897d24) Reviewed-on: https://go-review.googlesource.com/c/go/+/408821 Reviewed-by: Austin Clements <austin@google.com>
-rw-r--r--src/runtime/traceback.go15
1 files changed, 13 insertions, 2 deletions
diff --git a/src/runtime/traceback.go b/src/runtime/traceback.go
index 229e3a9105..56128dc882 100644
--- a/src/runtime/traceback.go
+++ b/src/runtime/traceback.go
@@ -82,6 +82,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
}
waspanic := false
cgoCtxt := gp.cgoCtxt
+ stack := gp.stack
printing := pcbuf == nil && callback == nil
// If the PC is zero, it's likely a nil function call.
@@ -114,7 +115,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
if !f.valid() {
if callback != nil || printing {
print("runtime: unknown pc ", hex(frame.pc), "\n")
- tracebackHexdump(gp.stack, &frame, 0)
+ tracebackHexdump(stack, &frame, 0)
}
if callback != nil {
throw("unknown pc")
@@ -174,12 +175,15 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
frame.fn = findfunc(frame.pc)
f = frame.fn
flag = f.flag
+ frame.lr = gp.m.curg.sched.lr
frame.sp = gp.m.curg.sched.sp
+ stack = gp.m.curg.stack
cgoCtxt = gp.m.curg.cgoCtxt
case funcID_systemstack:
// systemstack returns normally, so just follow the
// stack transition.
frame.sp = gp.m.curg.sched.sp
+ stack = gp.m.curg.stack
cgoCtxt = gp.m.curg.cgoCtxt
flag &^= funcFlag_SPWRITE
}
@@ -248,7 +252,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
}
if callback != nil || doPrint {
print("runtime: unexpected return pc for ", funcname(f), " called from ", hex(frame.lr), "\n")
- tracebackHexdump(gp.stack, &frame, lrPtr)
+ tracebackHexdump(stack, &frame, lrPtr)
}
if callback != nil {
throw("unknown caller pc")
@@ -477,6 +481,13 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
break
}
+ if frame.pc == frame.lr && frame.sp == frame.fp {
+ // If the next frame is identical to the current frame, we cannot make progress.
+ print("runtime: traceback stuck. pc=", hex(frame.pc), " sp=", hex(frame.sp), "\n")
+ tracebackHexdump(stack, &frame, frame.sp)
+ throw("traceback stuck")
+ }
+
// Unwind to next frame.
frame.fn = flr
frame.pc = frame.lr