zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

commit 9bfe827adedeffea591232fb713bea7b145c0add (tree)
parent 94355f1920d880837823812481140270d0dc631e
Author: Justus Klausecker <justus@klausecker.de>
Date:   Wed, 25 Mar 2026 11:20:21 +0100

Revert "std.heap.ArenaAllocator: Make `resize` and `free` check whether allocation is within current node more rigorously"

This reverts commit 589bcb2544fed9a3454908adba4a2f97f1405e9c.

The scenario presented in the reverted commit cannot actually happen.
Even if there are two contiguous arena nodes N1 and N2 and the `end_index`
of N1 points to somewhere in N2, a `resize` can never lead to an increase
of the `end_index` of N1 since it checks whether it's `<= size` first.
A `resize`/`free` *can* decrease `end_index`, but even if it is wrongly
assumed that some allocation that belongs to N2 actually belongs to N1
based on the `end_index` of N1, it can only ever be decreased to the start
of the buffer of N2. That's because a valid allocation of N2 logically
cannot be at any lower address than N2 itself. And any point still in N2
can never also be in N1, so there's no danger of overwriting any other
allocations of N1.

Diffstat:
Mlib/std/heap/ArenaAllocator.zig | 37++++++++++++-------------------------
1 file changed, 12 insertions(+), 25 deletions(-)

diff --git a/lib/std/heap/ArenaAllocator.zig b/lib/std/heap/ArenaAllocator.zig @@ -319,11 +319,6 @@ fn pushFreeList(arena: *ArenaAllocator, first: *Node, last: *Node) void { } } -fn sliceContainsSlice(container: []u8, slice: []u8) bool { - return @intFromPtr(slice.ptr) >= @intFromPtr(container.ptr) and - @intFromPtr(slice.ptr + slice.len) <= @intFromPtr(container.ptr + container.len); -} - fn alignedIndex(buf_ptr: [*]u8, end_index: usize, alignment: Alignment) usize { // Wrapping arithmetic to avoid overflows since `end_index` isn't bounded by // `size`. This is always ok since the max alignment in byte units is also @@ -548,17 +543,12 @@ fn resize(ctx: *anyopaque, memory: []u8, alignment: Alignment, new_len: usize, r assert(new_len > 0); const node = arena.loadFirstNode().?; - const buf = node.loadBuf(); - - if (!sliceContainsSlice(buf, memory)) { - // Not within current node. - return new_len <= memory.len; - } + const buf_ptr = @as([*]u8, @ptrCast(node)) + @sizeOf(Node); const cur_end_index = @atomicLoad(usize, &node.end_index, .monotonic); - - if (buf.ptr + cur_end_index != memory.ptr + memory.len) { - // It's not the most recent allocation, so it cannot be expanded. + if (buf_ptr + cur_end_index != memory.ptr + memory.len) { + // It's not the most recent allocation, so it cannot be expanded, + // but it's fine if they want to make it smaller. return new_len <= memory.len; } @@ -566,12 +556,15 @@ fn resize(ctx: *anyopaque, memory: []u8, alignment: Alignment, new_len: usize, r if (memory.len >= new_len) { break :new_end_index cur_end_index - (memory.len - new_len); } - if (buf.len - cur_end_index >= new_len - memory.len) { + const cur_buf_len: usize = node.loadBuf().len; + // Saturating arithmetic because `end_index` and `size` are not + // guaranteed to be in sync. + if (cur_buf_len -| cur_end_index >= new_len - memory.len) { break :new_end_index cur_end_index + (new_len - memory.len); } return false; }; - assert(buf.ptr + new_end_index == memory.ptr + new_len); + assert(buf_ptr + new_end_index == memory.ptr + new_len); return null == @cmpxchgStrong( usize, @@ -596,22 +589,16 @@ fn free(ctx: *anyopaque, memory: []u8, alignment: Alignment, ret_addr: usize) vo assert(memory.len > 0); const node = arena.loadFirstNode().?; - const buf = node.loadBuf(); - - if (!sliceContainsSlice(buf, memory)) { - // Not within current node; we cannot free it. - return; - } + const buf_ptr = @as([*]u8, @ptrCast(node)) + @sizeOf(Node); const cur_end_index = @atomicLoad(usize, &node.end_index, .monotonic); - - if (buf.ptr + cur_end_index != memory.ptr + memory.len) { + if (buf_ptr + cur_end_index != memory.ptr + memory.len) { // Not the most recent allocation; we cannot free it. return; } const new_end_index = cur_end_index - memory.len; - assert(buf.ptr + new_end_index == memory.ptr); + assert(buf_ptr + new_end_index == memory.ptr); _ = @cmpxchgStrong( usize,