From 9105cce340a7fa83c152cbd1a36f8bf557589074 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Fri, 24 Jul 2026 10:00:19 -0700 Subject: [PATCH] runtime: make out-of-memory failures fatal Match the Go runtime by terminating instead of unwinding when an allocator exhausts the heap. Recovering an OOM can leave allocator locks held and cannot safely continue when the panic path itself needs memory. --- src/runtime/gc_blocks.go | 4 ++-- src/runtime/gc_boehm.go | 2 +- src/runtime/gc_leaking.go | 2 +- src/runtime/panic.go | 12 ++++++++++++ 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/runtime/gc_blocks.go b/src/runtime/gc_blocks.go index 4d3577e52..0cb11c00f 100644 --- a/src/runtime/gc_blocks.go +++ b/src/runtime/gc_blocks.go @@ -416,7 +416,7 @@ func alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer { size += bytesPerBlock - 1 if size < rawSize { // The size overflowed. - runtimePanicAt(returnAddress(0), "out of memory") + runtimeFatal("out of memory") } neededBlocks := size / bytesPerBlock size = neededBlocks * bytesPerBlock @@ -465,7 +465,7 @@ func alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer { // Unfortunately the heap could not be increased. This // happens on baremetal systems for example (where all // available RAM has already been dedicated to the heap). - runtimePanicAt(returnAddress(0), "out of memory") + runtimeFatal("out of memory") } // Set the block states. diff --git a/src/runtime/gc_boehm.go b/src/runtime/gc_boehm.go index 2b2159f5d..a0adc93d3 100644 --- a/src/runtime/gc_boehm.go +++ b/src/runtime/gc_boehm.go @@ -87,7 +87,7 @@ func alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer { gcResumeWorld() gcLock.Unlock() if ptr == nil { - runtimePanic("gc: out of memory") + runtimeFatal("gc: out of memory") } return ptr diff --git a/src/runtime/gc_leaking.go b/src/runtime/gc_leaking.go index 51ae247f7..1467cb6bc 100644 --- a/src/runtime/gc_leaking.go +++ b/src/runtime/gc_leaking.go @@ -53,7 +53,7 @@ func alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer { continue } // Failed to make the heap bigger, so we must really be out of memory. - runtimePanic("out of memory") + runtimeFatal("out of memory") } gcLock.Unlock() diff --git a/src/runtime/panic.go b/src/runtime/panic.go index c8dc027fd..3c5b4029a 100644 --- a/src/runtime/panic.go +++ b/src/runtime/panic.go @@ -91,6 +91,18 @@ func runtimePanic(msg string) { runtimePanicAt(returnAddress(0), msg) } +// runtimeFatal terminates for runtime failures that cannot safely be +// recovered, such as exhausting the heap. +func runtimeFatal(msg string) { + if panicStrategy() == tinygo.PanicStrategyTrap { + trap() + } + printstring("fatal error: ") + printstring(msg) + printnl() + abort() +} + func runtimePanicAt(addr unsafe.Pointer, msg string) { if panicStrategy() == tinygo.PanicStrategyTrap { trap()