Skip to content

Commit 45cc5b5

Browse files
aykevldeadprogram
authored andcommitted
runtime: disallow defer in interrupts
This often doesn't work because there might not be a current task to push the defer frame to. It will instead show an unhelpful nil pointer dereference panic. We could make this work with a separate defer stack for interrupts (as if they were newly started goroutines) but that is difficult with multiple interrupts happening at the same time (we shouldn't jump to a previous interrupt in `panic()`!). So instead, disable defer altogether in interrupts and adjust panic/recover accordingly.
1 parent c6acaa9 commit 45cc5b5

File tree

1 file changed

+15
-3
lines changed

1 file changed

+15
-3
lines changed

src/runtime/panic.go

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package runtime
22

33
import (
44
"internal/task"
5+
"runtime/interrupt"
56
"unsafe"
67
)
78

@@ -50,7 +51,9 @@ func _panic(message interface{}) {
5051
if panicStrategy() == panicStrategyTrap {
5152
trap()
5253
}
53-
if supportsRecover() {
54+
// Note: recover is not supported inside interrupts.
55+
// (This could be supported, like defer, but we currently don't).
56+
if supportsRecover() && !interrupt.In() {
5457
frame := (*deferFrame)(task.Current().DeferFrame)
5558
if frame != nil {
5659
frame.PanicValue = message
@@ -95,6 +98,12 @@ func runtimePanicAt(addr unsafe.Pointer, msg string) {
9598
//go:inline
9699
//go:nobounds
97100
func setupDeferFrame(frame *deferFrame, jumpSP unsafe.Pointer) {
101+
if interrupt.In() {
102+
// Defer is not currently allowed in interrupts.
103+
// We could add support for this, but since defer might also allocate
104+
// (especially in loops) it might not be a good idea anyway.
105+
runtimePanicAt(returnAddress(0), "defer in interrupt")
106+
}
98107
currentTask := task.Current()
99108
frame.Previous = (*deferFrame)(currentTask.DeferFrame)
100109
frame.JumpSP = jumpSP
@@ -122,8 +131,11 @@ func destroyDeferFrame(frame *deferFrame) {
122131
// useParentFrame is set when the caller of runtime._recover has a defer frame
123132
// itself. In that case, recover() shouldn't check that frame but one frame up.
124133
func _recover(useParentFrame bool) interface{} {
125-
if !supportsRecover() {
126-
// Compiling without stack unwinding support, so make this a no-op.
134+
if !supportsRecover() || interrupt.In() {
135+
// Either we're compiling without stack unwinding support, or we're
136+
// inside an interrupt where panic/recover is not supported. Either way,
137+
// make this a no-op since panic() won't do any long jumps to a deferred
138+
// function.
127139
return nil
128140
}
129141
// TODO: somehow check that recover() is called directly by a deferred

0 commit comments

Comments
 (0)