zig

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

commit 5d3c8f4913884a4503e9f183e471b6090bb5bc92 (tree)
parent 41430a366f75eb7301deaca91d4aea3bbf61c8ec
Author: Erik Arvstedt <erik.arvstedt@gmail.com>
Date:   Mon, 12 Jun 2023 22:21:30 +0200

arena_allocator/reset: fix use after free

Previously, when the last buffer in `buffer_list` was retained after
deleting all other buffers, `buffer_list` wasn't updated and pointed
to a deleted buffer.

Diffstat:
Mlib/std/heap/arena_allocator.zig | 17+++++++++++++++++
1 file changed, 17 insertions(+), 0 deletions(-)

diff --git a/lib/std/heap/arena_allocator.zig b/lib/std/heap/arena_allocator.zig @@ -139,6 +139,7 @@ pub const ArenaAllocator = struct { // reset the state before we try resizing the buffers, so we definitely have reset the arena to 0. self.state.end_index = 0; if (maybe_first_node) |first_node| { + self.state.buffer_list.first = first_node; // perfect, no need to invoke the child_allocator if (first_node.data == total_size) return true; @@ -270,3 +271,19 @@ test "ArenaAllocator (reset with preheating)" { } } } + +test "ArenaAllocator (reset while retaining a buffer)" { + var arena_allocator = ArenaAllocator.init(std.testing.allocator); + defer arena_allocator.deinit(); + const a = arena_allocator.allocator(); + + // Create two internal buffers + _ = try a.alloc(u8, 1); + _ = try a.alloc(u8, 1000); + + // Check that we have at least two buffers + try std.testing.expect(arena_allocator.state.buffer_list.first.?.next != null); + + // This retains the first allocated buffer + try std.testing.expect(arena_allocator.reset(.{ .retain_with_limit = 1 })); +}