From e8199011f59a16655a136932ef98be219d110ca5 Mon Sep 17 00:00:00 2001 From: Nia Waldvogel Date: Sat, 29 Nov 2025 17:28:53 -0500 Subject: [PATCH] runtime (gc_blocks.go): clear full size of allocation This fixes a bug where runtime.alloc would not clear the padding from rounding the allocation up to a multiple of the block size. The GC still has to scan this, and this could result in permanent false positives (and therefore memory leaks). It also handles overflow in the size calculation. --- builder/sizes_test.go | 6 +++--- src/runtime/gc_blocks.go | 21 +++++++++++++++------ 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/builder/sizes_test.go b/builder/sizes_test.go index 16760218ca..c1d44f443d 100644 --- a/builder/sizes_test.go +++ b/builder/sizes_test.go @@ -42,9 +42,9 @@ func TestBinarySize(t *testing.T) { // This is a small number of very diverse targets that we want to test. tests := []sizeTest{ // microcontrollers - {"hifive1b", "examples/echo", 3884, 280, 0, 2268}, - {"microbit", "examples/serial", 2844, 360, 8, 2272}, - {"wioterminal", "examples/pininterrupt", 7349, 1491, 116, 6912}, + {"hifive1b", "examples/echo", 3896, 280, 0, 2268}, + {"microbit", "examples/serial", 2860, 360, 8, 2272}, + {"wioterminal", "examples/pininterrupt", 7361, 1491, 116, 6912}, // TODO: also check wasm. Right now this is difficult, because // wasm binaries are run through wasm-opt and therefore the diff --git a/src/runtime/gc_blocks.go b/src/runtime/gc_blocks.go index 85f4994908..408656b258 100644 --- a/src/runtime/gc_blocks.go +++ b/src/runtime/gc_blocks.go @@ -311,22 +311,31 @@ func alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer { return unsafe.Pointer(&zeroSizedAlloc) } + if interrupt.In() { + runtimePanicAt(returnAddress(0), "heap alloc in interrupt") + } + + // Round the size up to a multiple of blocks. + rawSize := size + size += bytesPerBlock - 1 if preciseHeap { + // Add space for the layout. size += align(unsafe.Sizeof(layout)) } - - if interrupt.In() { - runtimePanicAt(returnAddress(0), "heap alloc in interrupt") + if size < rawSize { + // The size overflowed. + runtimePanicAt(returnAddress(0), "out of memory") } + neededBlocks := size / bytesPerBlock + size = neededBlocks * bytesPerBlock // Make sure there are no concurrent allocations. The heap is not currently // designed for concurrent alloc/GC. gcLock.Lock() - gcTotalAlloc += uint64(size) + // Update the total allocation counters. + gcTotalAlloc += uint64(rawSize) gcMallocs++ - - neededBlocks := (size + (bytesPerBlock - 1)) / bytesPerBlock gcTotalBlocks += uint64(neededBlocks) // Continue looping until a run of free blocks has been found that fits the