Commit 7f1b2738 authored by Austin Clements's avatar Austin Clements

runtime: make throw safer to call

Currently, throw may grow the stack, which means whenever we call it
from a context where it's not safe to grow the stack, we first have to
switch to the system stack. This is pretty easy to get wrong.

Fix this by making throw switch to the system stack so it doesn't grow
the stack and is hence safe to call without a system stack switch at
the call site.

The only thing this complicates is badsystemstack itself, which would
now go into an infinite loop before printing anything (previously it
would also go into an infinite loop, but would at least print the
error first). Fix this by making badsystemstack do a direct write and
then crash hard.

Change-Id: Ic5b4a610df265e47962dcfa341cabac03c31c049
Reviewed-on: https://go-review.googlesource.com/93659
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarKeith Randall <khr@golang.org>
parent 9d59234c
......@@ -479,6 +479,7 @@ bad:
// Hide call from linker nosplit analysis.
MOVL $runtime·badsystemstack(SB), AX
CALL AX
INT $3
/*
* support for morestack
......
......@@ -424,6 +424,7 @@ bad:
// Bad: g is not gsignal, not g0, not curg. What is it?
MOVQ $runtime·badsystemstack(SB), AX
CALL AX
INT $3
/*
......
......@@ -310,6 +310,7 @@ bad:
// Hide call from linker nosplit analysis.
MOVL $runtime·badsystemstack(SB), AX
CALL AX
INT $3
/*
* support for morestack
......
......@@ -317,6 +317,7 @@ TEXT runtime·systemstack(SB),NOSPLIT,$0-4
// Hide call from linker nosplit analysis.
MOVW $runtime·badsystemstack(SB), R0
BL (R0)
B runtime·abort(SB)
switch:
// save our state in g->sched. Pretend to
......
......@@ -201,6 +201,7 @@ TEXT runtime·systemstack(SB), NOSPLIT, $0-8
// Hide call from linker nosplit analysis.
MOVD $runtime·badsystemstack(SB), R3
BL (R3)
B runtime·abort(SB)
switch:
// save our state in g->sched. Pretend to
......
......@@ -179,6 +179,7 @@ TEXT runtime·systemstack(SB), NOSPLIT, $0-8
// Hide call from linker nosplit analysis.
MOVV $runtime·badsystemstack(SB), R4
JAL (R4)
JAL runtime·abort(SB)
switch:
// save our state in g->sched. Pretend to
......
......@@ -180,6 +180,7 @@ TEXT runtime·systemstack(SB),NOSPLIT,$0-4
// Hide call from linker nosplit analysis.
MOVW $runtime·badsystemstack(SB), R4
JAL (R4)
JAL runtime·abort(SB)
switch:
// save our state in g->sched. Pretend to
......
......@@ -222,6 +222,7 @@ TEXT runtime·systemstack(SB), NOSPLIT, $0-8
MOVD $runtime·badsystemstack(SB), R12
MOVD R12, CTR
BL (CTR)
BL runtime·abort(SB)
switch:
// save our state in g->sched. Pretend to
......
......@@ -266,6 +266,7 @@ TEXT runtime·systemstack(SB), NOSPLIT, $0-8
// Hide call from linker nosplit analysis.
MOVD $runtime·badsystemstack(SB), R3
BL (R3)
BL runtime·abort(SB)
switch:
// save our state in g->sched. Pretend to
......
......@@ -148,9 +148,7 @@ func cgoCheckTypedBlock(typ *_type, src unsafe.Pointer, off, size uintptr) {
if i >= off && bits&bitPointer != 0 {
v := *(*unsafe.Pointer)(add(src, i))
if cgoIsGoPointer(v) {
systemstack(func() {
throw(cgoWriteBarrierFail)
})
}
}
hbits = hbits.next()
......@@ -183,9 +181,7 @@ func cgoCheckBits(src unsafe.Pointer, gcbits *byte, off, size uintptr) {
if bits&1 != 0 {
v := *(*unsafe.Pointer)(add(src, i))
if cgoIsGoPointer(v) {
systemstack(func() {
throw(cgoWriteBarrierFail)
})
}
}
}
......
......@@ -586,7 +586,11 @@ func sync_throw(s string) {
//go:nosplit
func throw(s string) {
// Everything throw does should be recursively nosplit so it
// can be called even when it's unsafe to grow the stack.
systemstack(func() {
print("fatal error: ", s, "\n")
})
gp := getg()
if gp.m.throwing == 0 {
gp.m.throwing = 1
......
......@@ -796,9 +796,7 @@ func casgstatus(gp *g, oldval, newval uint32) {
// GC time to finish and change the state to oldval.
for i := 0; !atomic.Cas(&gp.atomicstatus, oldval, newval); i++ {
if oldval == _Gwaiting && gp.atomicstatus == _Grunnable {
systemstack(func() {
throw("casgstatus: waiting for Gwaiting but is Grunnable")
})
}
// Help GC if needed.
// if gp.preemptscan && !gp.gcworkdone && (oldval == _Grunning || oldval == _Gsyscall) {
......@@ -2925,21 +2923,14 @@ func exitsyscall(dummy int32) {
_g_.m.locks++ // see comment in entersyscall
if getcallersp(unsafe.Pointer(&dummy)) > _g_.syscallsp {
// throw calls print which may try to grow the stack,
// but throwsplit == true so the stack can not be grown;
// use systemstack to avoid that possible problem.
systemstack(func() {
throw("exitsyscall: syscall frame is no longer valid")
})
}
_g_.waitsince = 0
oldp := _g_.m.p.ptr()
if exitsyscallfast() {
if _g_.m.mcache == nil {
systemstack(func() {
throw("lost mcache")
})
}
if trace.enabled {
if oldp != _g_.m.p.ptr() || _g_.m.syscalltick != _g_.m.p.ptr().syscalltick {
......@@ -2986,9 +2977,7 @@ func exitsyscall(dummy int32) {
mcall(exitsyscall0)
if _g_.m.mcache == nil {
systemstack(func() {
throw("lost mcache")
})
}
// Scheduler returned, so we're allowed to run now.
......
......@@ -1184,7 +1184,5 @@ func freeStackSpans() {
//go:nosplit
func morestackc() {
systemstack(func() {
throw("attempt to execute system stack code on user stack")
})
}
......@@ -53,8 +53,13 @@ func mcall(fn func(*g))
//go:noescape
func systemstack(fn func())
var badsystemstackMsg = "fatal: systemstack called from unexpected goroutine"
//go:nosplit
//go:nowritebarrierrec
func badsystemstack() {
throw("systemstack called from unexpected goroutine")
sp := stringStructOf(&badsystemstackMsg)
write(2, sp.str, int32(sp.len))
}
// memclrNoHeapPointers clears n bytes starting at ptr.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment