Skip to content

Commit e2bb03f

Browse files
committed
runtime: don't install a stack barrier in cgocallback_gofunc's frame
Currently the runtime can install stack barriers in any frame. However, the frame of cgocallback_gofunc is special: it's the one function that switches from a regular G stack to the system stack on return. Hence, the return PC slot in its frame on the G stack is actually used to save getg().sched.pc (so tracebacks appear to unwind to the last Go function running on that G), and not as an actual return PC for cgocallback_gofunc. Because of this, if we install a stack barrier in cgocallback_gofunc's return PC slot, when cgocallback_gofunc does return, it will move the stack barrier stub PC in to getg().sched.pc and switch back to the system stack. The rest of the runtime doesn't know how to deal with a stack barrier stub in sched.pc: nothing knows how to match it up with the G's stack barrier array and, when the runtime removes stack barriers, it doesn't know to undo the one in sched.pc. Hence, if the C code later returns back in to Go code, it will attempt to return through the stack barrier saved in sched.pc, which may no longer have correct unwinding information. Fix this by blacklisting cgocallback_gofunc's frame so the runtime won't install a stack barrier in it's return PC slot. Fixes #12238. Change-Id: I46aa2155df2fd050dd50de3434b62987dc4947b8 Reviewed-on: https://go-review.googlesource.com/13944 Reviewed-by: Russ Cox <[email protected]>
1 parent 0cced63 commit e2bb03f

File tree

3 files changed

+28
-5
lines changed

3 files changed

+28
-5
lines changed

src/runtime/asm_amd64p32.s

+6
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,12 @@ TEXT runtime·cgocallback(SB),NOSPLIT,$0-12
592592
MOVL 0, AX
593593
RET
594594

595+
// cgocallback_gofunc(FuncVal*, void *frame, uintptr framesize)
596+
// Not implemented.
597+
TEXT ·cgocallback_gofunc(SB),NOSPLIT,$0-12
598+
MOVL 0, AX
599+
RET
600+
595601
// void setg(G*); set g. for use by needm.
596602
// Not implemented.
597603
TEXT runtime·setg(SB), NOSPLIT, $0-4

src/runtime/mgcmark.go

+20-5
Original file line numberDiff line numberDiff line change
@@ -388,9 +388,10 @@ func scanstack(gp *g) {
388388
// frame because on LR machines this LR is not
389389
// on the stack.
390390
if gcphase == _GCscan && n != 0 {
391-
gcInstallStackBarrier(gp, frame)
392-
barrierOffset *= 2
393-
nextBarrier = sp + barrierOffset
391+
if gcInstallStackBarrier(gp, frame) {
392+
barrierOffset *= 2
393+
nextBarrier = sp + barrierOffset
394+
}
394395
} else if gcphase == _GCmarktermination {
395396
// We just scanned a frame containing
396397
// a return to a stack barrier. Since
@@ -509,12 +510,25 @@ func gcMaxStackBarriers(stackSize int) (n int) {
509510

510511
// gcInstallStackBarrier installs a stack barrier over the return PC of frame.
511512
//go:nowritebarrier
512-
func gcInstallStackBarrier(gp *g, frame *stkframe) {
513+
func gcInstallStackBarrier(gp *g, frame *stkframe) bool {
513514
if frame.lr == 0 {
514515
if debugStackBarrier {
515516
print("not installing stack barrier with no LR, goid=", gp.goid, "\n")
516517
}
517-
return
518+
return false
519+
}
520+
521+
if frame.fn.entry == cgocallback_gofuncPC {
522+
// cgocallback_gofunc doesn't return to its LR;
523+
// instead, its return path puts LR in g.sched.pc and
524+
// switches back to the system stack on which
525+
// cgocallback_gofunc was originally called. We can't
526+
// have a stack barrier in g.sched.pc, so don't
527+
// install one in this frame.
528+
if debugStackBarrier {
529+
print("not installing stack barrier over LR of cgocallback_gofunc, goid=", gp.goid, "\n")
530+
}
531+
return false
518532
}
519533

520534
// Save the return PC and overwrite it with stackBarrier.
@@ -538,6 +552,7 @@ func gcInstallStackBarrier(gp *g, frame *stkframe) {
538552
stkbar.savedLRPtr = lrUintptr
539553
stkbar.savedLRVal = uintptr(*lrPtr)
540554
*lrPtr = uintreg(stackBarrierPC)
555+
return true
541556
}
542557

543558
// gcRemoveStackBarriers removes all stack barriers installed in gp's stack.

src/runtime/traceback.go

+2
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ var (
4848
systemstack_switchPC uintptr
4949
systemstackPC uintptr
5050
stackBarrierPC uintptr
51+
cgocallback_gofuncPC uintptr
5152

5253
gogoPC uintptr
5354

@@ -75,6 +76,7 @@ func tracebackinit() {
7576
systemstack_switchPC = funcPC(systemstack_switch)
7677
systemstackPC = funcPC(systemstack)
7778
stackBarrierPC = funcPC(stackBarrier)
79+
cgocallback_gofuncPC = funcPC(cgocallback_gofunc)
7880

7981
// used by sigprof handler
8082
gogoPC = funcPC(gogo)

0 commit comments

Comments
 (0)