fix UAF in grow

Grow needs to allocate and might fail midway. It tries to handle this
using "undo" pattern, and restoring old state on error. But this is
exactly what steps into UAF, as, on error, both errdefer and defer are
run, and the old data is freed.

Instead, use a more robust "reservation" pattern, where we first
fallibly resrve all the resources we need, without applying any changes,
and than do the actual change once we are sure that cannot fail.
pull/8249/head
Alex Kladov 2025-08-15 18:06:25 +01:00
parent 0930b2daff
commit 4c4d3cfc3f
1 changed files with 7 additions and 10 deletions

View File

@ -287,22 +287,19 @@ pub fn grow(self: *Atlas, alloc: Allocator, size_new: u32) Allocator.Error!void
assert(size_new >= self.size);
if (size_new == self.size) return;
try self.nodes.ensureUnusedCapacity(alloc, 1);
const data_new = try alloc.alloc(u8, size_new * size_new * self.format.depth());
errdefer comptime unreachable; // End resource reservation phase.
// Preserve our old values so we can copy the old data
const data_old = self.data;
const size_old = self.size;
// Allocate our new data
self.data = try alloc.alloc(u8, size_new * size_new * self.format.depth());
self.data = data_new;
defer alloc.free(data_old);
errdefer {
alloc.free(self.data);
self.data = data_old;
}
// Add our new rectangle for our added righthand space. We do this
// right away since its the only operation that can fail and we want
// to make error cleanup easier.
try self.nodes.append(alloc, .{
// Add our new rectangle for our added righthand space.
self.nodes.appendAssumeCapacity(alloc, .{
.x = size_old - 1,
.y = 1,
.width = size_new - size_old,