From d89ccabef064661794269c0de726021736d72b51 Mon Sep 17 00:00:00 2001 From: Martin Vogel Date: Mon, 14 Sep 2026 21:26:06 +0200 Subject: [PATCH] fix(mem): the parked result image is declared defined for MemorySanitizer The MSan lane on 9724d903 stopped in the extraction suite's spill round-trip test: uninitialized bytes in fwrite at offset 4087 of a 6,952-byte region, the parked result's arena block (the stack overflow that followed is MemorySanitizer's own report path failing on the nested bug). The park writes a result as an opaque image: the record header, a struct copy, and the compacted arena block. Both carry padding bytes no code ever wrote, inside structs and between objects, and MSan tracks that mark through the compaction's memcpy, so zeroing the destination block only moved the report (offset 6714 in the local MSan container). The image is read back whole and never interpreted byte by byte; the padding content is irrelevant on disk. Under MSan the header and the block are declared defined right before the write (__msan_unpoison, compiled in only when the build is instrumented); every other build compiles the annotation to nothing. The exact block is also zeroed once when it is created, so the gaps between objects are deterministic in the spill file. Verification: extraction, arena and parallel suites under ASan on the fixed tree; the extraction suite in the local MSan container image passes 348 / 0 with no sanitizer report (it reproduced the CI report exactly before the fix). Signed-off-by: Martin Vogel --- internal/cbm/result_spill.c | 20 ++++++++++++++++++++ src/foundation/arena.c | 9 +++++++++ 2 files changed, 29 insertions(+) diff --git a/internal/cbm/result_spill.c b/internal/cbm/result_spill.c index b75f06fb5..b79dc0e6c 100644 --- a/internal/cbm/result_spill.c +++ b/internal/cbm/result_spill.c @@ -21,6 +21,24 @@ #include "foundation/log.h" #include "foundation/mem_core.h" +/* The park writes a result as an opaque image: the record header (a struct + * copy) and the compacted arena block. Both carry padding bytes no code ever + * wrote -- inside structs, between objects -- and MemorySanitizer tracks + * that mark through the compaction's memcpy, so it refuses the fwrite of an + * image that is read back whole and never interpreted byte by byte (CI MSan + * lane on #2202: offset 4087, then 6714, of a 6,952-byte block). Under MSan + * the image is declared defined right before the write; every other build + * compiles this to nothing. */ +#if defined(__has_feature) +#if __has_feature(memory_sanitizer) +#include +#define SPILL_IMAGE_DEFINED(p, n) __msan_unpoison((p), (n)) +#endif +#endif +#ifndef SPILL_IMAGE_DEFINED +#define SPILL_IMAGE_DEFINED(p, n) ((void)0) +#endif + #include #include #include @@ -133,6 +151,8 @@ bool cbm_result_spill_park(cbm_result_spill_t *sp, int writer, int slot, CBMFile hdr.old_base = (uint64_t)(uintptr_t)result->arena.blocks[0]; hdr.header = *result; hdr.header.cached_tree = NULL; /* never on disk: the loader gets no tree */ + SPILL_IMAGE_DEFINED(&hdr, sizeof(hdr)); + SPILL_IMAGE_DEFINED(result->arena.blocks[0], hdr.block_len); cbm_mutex_lock(&f->mu); uint64_t offset = f->end; bool ok = SPILL_SEEK(f->fp, offset) == 0 && fwrite(&hdr, sizeof(hdr), 1, f->fp) == 1 && diff --git a/src/foundation/arena.c b/src/foundation/arena.c index 20d604de0..82a2f2af6 100644 --- a/src/foundation/arena.c +++ b/src/foundation/arena.c @@ -45,6 +45,15 @@ void cbm_arena_init_exact(CBMArena *a, size_t bytes) { size_t block = (bytes + ARENA_ALIGN) & ~(size_t)ARENA_ALIGN; cbm_arena_init_sized(a, block); a->grow_size = CBM_ARENA_DEFAULT_BLOCK_SIZE; + /* An exact block is a compacted result's image and may be written to + * disk whole (result_spill): the alignment gaps between objects and the + * padding inside structs are never written by the copy, and MemorySanitizer + * refuses an fwrite of uninitialized bytes (CI MSan lane on #2202, offset + * 4087 of a 6,952-byte block). Zeroed once here, every byte of the image + * is defined; the cost is one pass over memory the copy is about to touch. */ + if (a->blocks[0]) { + memset(a->blocks[0], 0, a->block_sizes[0]); + } } static int arena_grow(CBMArena *a, size_t min_size) {