From a80b1dd2bbeaf194981d84a3ba351ba1173f6d65 Mon Sep 17 00:00:00 2001 From: Michael Bentley Date: Sun, 25 Oct 2026 15:06:46 -0400 Subject: [PATCH] Account for committed memory when VMChunkCreate fails after ChunkInit starts. VMChunkCreate maps the chunk structure's grain with vmArenaMap, which adds it to arena->committed, but its failChunkInit path unmapped it with the raw VMUnmap, which does not subtract it. A grow refused at the commit limit therefore left the arena over-counting committed memory, and mps_arena_destroy failed the check at arenavm.c:799. It now calls VMChunkFinish, which unmaps the chunk's overhead through vmArenaUnmap and finishes the VM. ChunkInit no longer calls the arena class's chunkFinish when ArenaFreeLandInsert fails. Its caller undoes the class's chunkInit, as it must for every other failure; calling it in both places made the VM arena unmap and finish the chunk's VM twice. Fixes GitHub issue #319 . --- code/arenavm.c | 9 ++++++++- code/tract.c | 6 ++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/code/arenavm.c b/code/arenavm.c index 50708e9563..ad3f3bc619 100644 --- a/code/arenavm.c +++ b/code/arenavm.c @@ -91,6 +91,7 @@ typedef struct VMArenaStruct { /* VM arena structure */ static void VMFree(Addr base, Size size, Pool pool); static Size VMPurgeSpare(Arena arena, Size size); static Size vmArenaUnmapSpare(Arena arena, Size size, Chunk filter); +static void VMChunkFinish(Chunk chunk); DECLARE_CLASS(Arena, VMArena, AbstractArena); static void VMCompact(Arena arena, Trace trace); static void pageDescUnmap(VMChunk vmChunk, Index basePI, Index limitPI); @@ -350,7 +351,13 @@ static Res VMChunkCreate(Chunk *chunkReturn, VMArena vmArena, Size size) return ResOK; failChunkInit: - VMUnmap(vm, VMBase(vm), chunkStructLimit); + /* ChunkInit leaves undoing VMChunkInit to its caller. VMChunkFinish + unmaps the chunk's overhead through vmArenaUnmap, so that + arena->committed stays equal to what is mapped, and finishes the VM. + See GitHub issue #319 . */ + VMChunkFinish(VMChunk2Chunk(vmChunk)); + return res; + failChunkMap: failChunkAlloc: failBootInit: diff --git a/code/tract.c b/code/tract.c index baa01ef611..aad755992a 100644 --- a/code/tract.c +++ b/code/tract.c @@ -238,9 +238,11 @@ Res ChunkInit(Chunk chunk, Arena arena, Addr base, Addr limit, Size reserved, return ResOK; failLandInsert: - Method(Arena, arena, chunkFinish)(chunk); /* .no-clean: No clean-ups needed past this point for boot, as we will - discard the chunk. */ + discard the chunk. The caller undoes the arena class's chunkInit, as + it must for every failure after it: calling chunkFinish here as well + made the VM arena unmap and finish the chunk's VM twice. See GitHub + issue #319 . */ failClassInit: failAllocTable: return res;