mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-13 15:33:40 +00:00
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.
This commit is contained in:
committed by
Ron Evans
parent
c6acaa981d
commit
45cc5b58cd
+15
-3
@@ -2,6 +2,7 @@ package runtime
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"internal/task"
|
"internal/task"
|
||||||
|
"runtime/interrupt"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -50,7 +51,9 @@ func _panic(message interface{}) {
|
|||||||
if panicStrategy() == panicStrategyTrap {
|
if panicStrategy() == panicStrategyTrap {
|
||||||
trap()
|
trap()
|
||||||
}
|
}
|
||||||
if supportsRecover() {
|
// Note: recover is not supported inside interrupts.
|
||||||
|
// (This could be supported, like defer, but we currently don't).
|
||||||
|
if supportsRecover() && !interrupt.In() {
|
||||||
frame := (*deferFrame)(task.Current().DeferFrame)
|
frame := (*deferFrame)(task.Current().DeferFrame)
|
||||||
if frame != nil {
|
if frame != nil {
|
||||||
frame.PanicValue = message
|
frame.PanicValue = message
|
||||||
@@ -95,6 +98,12 @@ func runtimePanicAt(addr unsafe.Pointer, msg string) {
|
|||||||
//go:inline
|
//go:inline
|
||||||
//go:nobounds
|
//go:nobounds
|
||||||
func setupDeferFrame(frame *deferFrame, jumpSP unsafe.Pointer) {
|
func setupDeferFrame(frame *deferFrame, jumpSP unsafe.Pointer) {
|
||||||
|
if interrupt.In() {
|
||||||
|
// Defer is not currently allowed in interrupts.
|
||||||
|
// We could add support for this, but since defer might also allocate
|
||||||
|
// (especially in loops) it might not be a good idea anyway.
|
||||||
|
runtimePanicAt(returnAddress(0), "defer in interrupt")
|
||||||
|
}
|
||||||
currentTask := task.Current()
|
currentTask := task.Current()
|
||||||
frame.Previous = (*deferFrame)(currentTask.DeferFrame)
|
frame.Previous = (*deferFrame)(currentTask.DeferFrame)
|
||||||
frame.JumpSP = jumpSP
|
frame.JumpSP = jumpSP
|
||||||
@@ -122,8 +131,11 @@ func destroyDeferFrame(frame *deferFrame) {
|
|||||||
// useParentFrame is set when the caller of runtime._recover has a defer frame
|
// useParentFrame is set when the caller of runtime._recover has a defer frame
|
||||||
// itself. In that case, recover() shouldn't check that frame but one frame up.
|
// itself. In that case, recover() shouldn't check that frame but one frame up.
|
||||||
func _recover(useParentFrame bool) interface{} {
|
func _recover(useParentFrame bool) interface{} {
|
||||||
if !supportsRecover() {
|
if !supportsRecover() || interrupt.In() {
|
||||||
// Compiling without stack unwinding support, so make this a no-op.
|
// Either we're compiling without stack unwinding support, or we're
|
||||||
|
// inside an interrupt where panic/recover is not supported. Either way,
|
||||||
|
// make this a no-op since panic() won't do any long jumps to a deferred
|
||||||
|
// function.
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// TODO: somehow check that recover() is called directly by a deferred
|
// TODO: somehow check that recover() is called directly by a deferred
|
||||||
|
|||||||
Reference in New Issue
Block a user