Less arena-reuse (retain) in Debug

Arena reuse/retain can hide UAF issues, often resulting in a crash that is more
symptom than cause (far from where the error actually is). Removing this, lets
us better utilize the DebugAllocator's UAF-detection.

Also, when running WPT tests (-Dwpt_extensions) limit console logging to 100
values (a few tests writer millions of values, which is annoying and just
destroys the terminal).
This commit is contained in:
Karl Seguin
2026-06-02 13:00:17 +08:00
parent 1895d8f58d
commit a7355a5762
3 changed files with 22 additions and 7 deletions

View File

@@ -28,6 +28,9 @@ const ArenaPool = @This();
const IS_DEBUG = builtin.mode == .Debug;
// In Debug, disable pooling to better catch UAF.
const SAFETY = IS_DEBUG == true and builtin.is_test == false;
pub const BucketSize = enum { tiny, small, medium, large };
const Bucket = struct {
@@ -187,7 +190,8 @@ pub fn release(self: *ArenaPool, allocator: Allocator) void {
}
}
if (bucket.free_list_len >= bucket.free_list_max) {
if ((comptime SAFETY) or bucket.free_list_len >= bucket.free_list_max) {
// In Debug, we never pool. It can mask UAF bugs.
arena.deinit();
self.entry_pool.destroy(entry);
return;
@@ -200,12 +204,14 @@ pub fn release(self: *ArenaPool, allocator: Allocator) void {
pub fn reset(_: *const ArenaPool, allocator: Allocator, retain: usize) void {
const arena: *ArenaAllocator = @ptrCast(@alignCast(allocator.ptr));
_ = arena.reset(.{ .retain_with_limit = retain });
// In Debug, free_all, it's less likely to hide things
_ = arena.reset(if (comptime SAFETY) .free_all else .{ .retain_with_limit = retain });
}
pub fn resetRetain(_: *const ArenaPool, allocator: Allocator) void {
const arena: *ArenaAllocator = @ptrCast(@alignCast(allocator.ptr));
_ = arena.reset(.retain_capacity);
// In Debug, free_all, it's less likely to hide things
_ = arena.reset(if (comptime SAFETY) .free_all else .retain_capacity);
}
const testing = std.testing;

View File

@@ -175,7 +175,7 @@ const ValueWriter = struct {
stack: ?[]const u8 = null,
pub fn format(self: ValueWriter, writer: *std.io.Writer) !void {
for (self.values, 1..) |value, i| {
for (self.valuesToLog(), 1..) |value, i| {
try writer.print("\n arg({d}): {f}", .{ i, value });
}
if (self.stack) |s| {
@@ -185,7 +185,7 @@ const ValueWriter = struct {
pub fn logFmt(self: ValueWriter, _: []const u8, writer: anytype) !void {
var buf: [32]u8 = undefined;
for (self.values, 0..) |value, i| {
for (self.valuesToLog(), 0..) |value, i| {
const name = try std.fmt.bufPrint(&buf, "param.{d}", .{i});
try writer.write(name, value);
}
@@ -193,11 +193,20 @@ const ValueWriter = struct {
pub fn jsonStringify(self: ValueWriter, writer: *std.json.Stringify) !void {
try writer.beginArray();
for (self.values) |value| {
for (self.valuesToLog()) |value| {
try writer.write(value);
}
return writer.endArray();
}
fn valuesToLog(self: ValueWriter) []js.Value {
if (lp.build_config.wpt_extensions) {
// A few WPT tests print HUGE arrays, it's at best, annoying when
// running it locally
return self.values[0..@min(self.values.len, 100)];
}
return self.values;
}
};
pub const JsApi = struct {

View File

@@ -535,7 +535,7 @@ pub fn frameCreated(bc: *CDP.BrowserContext, frame: *Frame) !void {
const in_commit = bc.session.pendingPage() != null;
if (!in_commit) {
_ = bc.cdp.frame_arena.reset(.{ .retain_with_limit = 1024 * 512 });
bc.cdp.browser.arena_pool.reset(bc.frame_arena, 1024 * 512);
}
for (bc.isolated_worlds.items) |isolated_world| {