Skip to content

Commit

Permalink
runtime: allow crash from gsignal stack
Browse files Browse the repository at this point in the history
The uses of onM in dopanic/startpanic are okay even from the signal stack.

Fixes golang#8666.

LGTM=khr
R=khr
CC=golang-codereviews
https://golang.org/cl/134710043
  • Loading branch information
rsc committed Sep 11, 2014
1 parent f956740 commit 1d550b8
Show file tree
Hide file tree
Showing 9 changed files with 104 additions and 2 deletions.
17 changes: 17 additions & 0 deletions src/runtime/asm_386.s
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,23 @@ TEXT runtime·mcall(SB), NOSPLIT, $0-4
TEXT runtime·switchtoM(SB), NOSPLIT, $0-4
RET

// func onM_signalok(fn func())
TEXT runtime·onM_signalok(SB), NOSPLIT, $0-4
get_tls(CX)
MOVL g(CX), AX // AX = g
MOVL g_m(AX), BX // BX = m
MOVL m_gsignal(BX), DX // DX = gsignal
CMPL AX, DX
JEQ ongsignal
JMP runtime·onM(SB)

ongsignal:
MOVL fn+0(FP), DI // DI = fn
MOVL DI, DX
MOVL 0(DI), DI
CALL DI
RET

// func onM(fn func())
TEXT runtime·onM(SB), NOSPLIT, $0-4
MOVL fn+0(FP), DI // DI = fn
Expand Down
17 changes: 17 additions & 0 deletions src/runtime/asm_amd64.s
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,23 @@ TEXT runtime·mcall(SB), NOSPLIT, $0-8
TEXT runtime·switchtoM(SB), NOSPLIT, $0-8
RET

// func onM_signalok(fn func())
TEXT runtime·onM_signalok(SB), NOSPLIT, $0-8
get_tls(CX)
MOVQ g(CX), AX // AX = g
MOVQ g_m(AX), BX // BX = m
MOVQ m_gsignal(BX), DX // DX = gsignal
CMPQ AX, DX
JEQ ongsignal
JMP runtime·onM(SB)

ongsignal:
MOVQ fn+0(FP), DI // DI = fn
MOVQ DI, DX
MOVQ 0(DI), DI
CALL DI
RET

// func onM(fn func())
TEXT runtime·onM(SB), NOSPLIT, $0-8
MOVQ fn+0(FP), DI // DI = fn
Expand Down
17 changes: 17 additions & 0 deletions src/runtime/asm_amd64p32.s
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,23 @@ TEXT runtime·mcall(SB), NOSPLIT, $0-4
TEXT runtime·switchtoM(SB), NOSPLIT, $0-4
RET

// func onM_signalok(fn func())
TEXT runtime·onM_signalok(SB), NOSPLIT, $0-4
get_tls(CX)
MOVL g(CX), AX // AX = g
MOVL g_m(AX), BX // BX = m
MOVL m_gsignal(BX), DX // DX = gsignal
CMPL AX, DX
JEQ ongsignal
JMP runtime·onM(SB)

ongsignal:
MOVL fn+0(FP), DI // DI = fn
MOVL DI, DX
MOVL 0(DI), DI
CALL DI
RET

// func onM(fn func())
TEXT runtime·onM(SB), NOSPLIT, $0-4
MOVL fn+0(FP), DI // DI = fn
Expand Down
15 changes: 15 additions & 0 deletions src/runtime/asm_arm.s
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,21 @@ TEXT runtime·switchtoM(SB),NOSPLIT,$0-4
BL (R0) // clobber lr to ensure push {lr} is kept
RET

// func onM_signalok(fn func())
TEXT runtime·onM_signalok(SB), NOSPLIT, $-4-4
MOVW g_m(g), R1
MOVW m_gsignal(R1), R2
CMP g, R2
B.EQ ongsignal
B runtime·onM(SB)

ongsignal:
MOVW fn+0(FP), R0
MOVW R0, R7
MOVW 0(R0), R0
BL (R0)
RET

// func onM(fn func())
TEXT runtime·onM(SB),NOSPLIT,$0-4
MOVW fn+0(FP), R0 // R0 = fn
Expand Down
16 changes: 16 additions & 0 deletions src/runtime/crash_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,14 @@ func TestMainGoroutineId(t *testing.T) {
}
}

func TestBreakpoint(t *testing.T) {
output := executeTest(t, breakpointSource, nil)
want := "runtime.Breakpoint()"
if !strings.Contains(output, want) {
t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
}
}

const crashSource = `
package main
Expand Down Expand Up @@ -380,3 +388,11 @@ func main() {
panic("test")
}
`

const breakpointSource = `
package main
import "runtime"
func main() {
runtime.Breakpoint()
}
`
4 changes: 2 additions & 2 deletions src/runtime/panic.go
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ func gorecover(argp uintptr) interface{} {

//go:nosplit
func startpanic() {
onM(startpanic_m)
onM_signalok(startpanic_m)
}

//go:nosplit
Expand All @@ -381,7 +381,7 @@ func dopanic(unused int) {
mp.ptrarg[0] = unsafe.Pointer(gp)
mp.scalararg[0] = getcallerpc((unsafe.Pointer)(&unused))
mp.scalararg[1] = getcallersp((unsafe.Pointer)(&unused))
onM(dopanic_m) // should never return
onM_signalok(dopanic_m) // should never return
*(*int)(nil) = 0
}

Expand Down
1 change: 1 addition & 0 deletions src/runtime/proc.c
Original file line number Diff line number Diff line change
Expand Up @@ -2398,6 +2398,7 @@ gfpurge(P *p)
runtime·unlock(&runtime·sched.gflock);
}

#pragma textflag NOSPLIT
void
runtime·Breakpoint(void)
{
Expand Down
1 change: 1 addition & 0 deletions src/runtime/runtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -823,6 +823,7 @@ int32 runtime·mcount(void);
int32 runtime·gcount(void);
void runtime·mcall(void(**)(G*));
void runtime·onM(void(**)(void));
void runtime·onMsignal(void(**)(void));
uint32 runtime·fastrand1(void);
void runtime·rewindmorestack(Gobuf*);
int32 runtime·timediv(int64, int32, int32*);
Expand Down
18 changes: 18 additions & 0 deletions src/runtime/stubs.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,24 @@ func mcall(fn func(*g))
//go:noescape
func onM(fn func())

// onMsignal is like onM but is allowed to be used in code that
// might run on the gsignal stack. Code running on a signal stack
// may be interrupting an onM sequence on the main stack, so
// if the onMsignal calling sequence writes to ptrarg/scalararg,
// it must first save the old values and then restore them when
// finished. As an exception to the rule, it is fine not to save and
// restore the values if the program is trying to crash rather than
// return from the signal handler.
// Once all the runtime is written in Go, there will be no ptrarg/scalararg
// and the distinction between onM and onMsignal (and perhaps mcall)
// can go away.
//
// If onMsignal is called from a gsignal stack, it invokes fn directly,
// without a stack switch. Otherwise onMsignal behaves like onM.
//
//go:noescape
func onM_signalok(fn func())

func badonm() {
gothrow("onM called from signal goroutine")
}
Expand Down

0 comments on commit 1d550b8

Please sign in to comment.