From 20e22d450742b877cd1d78ed6a46fa2cbc171daf 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 | 23 ++++++++++++++++------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/builder/sizes_test.go b/builder/sizes_test.go index 16760218c..c1d44f443 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 85f499490..408656b25 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 preciseHeap { - size += align(unsafe.Sizeof(layout)) - } - 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 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