authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-07-09 10:21:06+01:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2026-07-09 15:41:19-04:00
loga03711b1e3921b2e088e4ee05277497e6143355c
treeef92dad92dd48c40ce58f583dea9347ed9e707f6
parent0fe0d69c63888d92a959ff20ad032348b5a2bd53

MappedFile: fix pathological case causing extreme file sizes

The old logic here meant that if you had a parent node with a large number of small child nodes being added to it, the parent node would grow in small steps instead of exponentially. If there was insufficient space for the parent node to grow, it would jump over its siblings, leaving a vacant space. If the siblings were also hitting this case, the nodes would end up "leapfrogging" over one another constantly, never re-using the vacant region before them (due to the node allocation logic in `MappedFile` currently being quite simplistic). In these conditions, because the nodes were adding wasted space every time they wanted to grow just *slightly*, the file size could get truly ridiculous---at worst, binaries which should be on the order of a few hundred megabytes could potentially reach the order of 100 *gigabytes*. The `MappedFile.growth_factor` constant solves exactly this class of problem by using exponential growth to bound the number of wasted bytes in the file, so the fix is simply to actually use it when expanding a parent node to make space for a new child.

1 files changed, 1 insertions(+), 1 deletions(-)

src/link/MappedFile.zig+1-1
...@@ -572,7 +572,7 @@ fn addNode(mf: *MappedFile, gpa: std.mem.Allocator, opts: struct {...@@ -572,7 +572,7 @@ fn addNode(mf: *MappedFile, gpa: std.mem.Allocator, opts: struct {
572 .none => {572 .none => {
573 _, const parent_size = opts.parent.location(mf).resolve(mf);573 _, const parent_size = opts.parent.location(mf).resolve(mf);
574 if (new_end > parent_size)574 if (new_end > parent_size)
575 try opts.parent.resize(mf, gpa, new_end);575 try opts.parent.resize(mf, gpa, new_end +| new_end / growth_factor);
576 },576 },
577 else => |next_ni| {577 else => |next_ni| {
578 const next_offset, _ = next_ni.location(mf).resolve(mf);578 const next_offset, _ = next_ni.location(mf).resolve(mf);