mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-07-30 17:25:58 -04:00
Merge pull request #3011 from lightpanda-io/rc-overflow
overflow: Make RC a u32
This commit is contained in:
@@ -19,6 +19,17 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=rcStress>
|
||||
// regression for when RC was generic, held a u8 and would overflow
|
||||
const stress = new URLSearchParams('a=1&b=2');
|
||||
const iters = [];
|
||||
for (let i = 0; i < 300; i++) {
|
||||
iters.push(stress.keys());
|
||||
}
|
||||
// as long as this test doesn't cause a crash, we're ok
|
||||
testing.expectTrue(true);
|
||||
</script>
|
||||
|
||||
<script id=urlSearchParams>
|
||||
const inputs = [
|
||||
[["over", "9000!!"], ["abc", "123"], ["key1", ""], ["key2", ""]],
|
||||
|
||||
@@ -32,7 +32,7 @@ const AbstractRange = @This();
|
||||
|
||||
pub const _prototype_root = true;
|
||||
|
||||
_rc: lp.RC(u8) = .{},
|
||||
_rc: lp.RC = .{},
|
||||
_type: Type,
|
||||
_arena: Allocator,
|
||||
_end_offset: u32,
|
||||
|
||||
@@ -35,7 +35,7 @@ const Blob = @This();
|
||||
pub const _prototype_root = true;
|
||||
|
||||
_type: Type,
|
||||
_rc: lp.RC(u32),
|
||||
_rc: lp.RC,
|
||||
|
||||
_arena: Allocator,
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ const Allocator = std.mem.Allocator;
|
||||
/// generateKey(), deriveKey(), importKey(), or unwrapKey().
|
||||
const CryptoKey = @This();
|
||||
|
||||
_rc: lp.RC(u8) = .{},
|
||||
_rc: lp.RC = .{},
|
||||
_arena: Allocator = undefined,
|
||||
/// Algorithm being used.
|
||||
_type: Type,
|
||||
|
||||
@@ -30,7 +30,7 @@ const DOMMatrixReadOnly = @This();
|
||||
pub const _prototype_root = true;
|
||||
|
||||
_type: Type,
|
||||
_rc: lp.RC(u8),
|
||||
_rc: lp.RC,
|
||||
_arena: Allocator,
|
||||
|
||||
// Stored column-major, matching the spec's mAB naming where A is the column
|
||||
|
||||
@@ -29,7 +29,7 @@ pub const FilterOpts = NodeFilter.FilterOpts;
|
||||
|
||||
const DOMNodeIterator = @This();
|
||||
|
||||
_rc: lp.RC(u8) = .{},
|
||||
_rc: lp.RC = .{},
|
||||
_root: *Node,
|
||||
_what_to_show: u32,
|
||||
_filter: NodeFilter,
|
||||
|
||||
@@ -31,7 +31,7 @@ const DOMPointReadOnly = @This();
|
||||
pub const _prototype_root = true;
|
||||
|
||||
_type: Type,
|
||||
_rc: lp.RC(u8),
|
||||
_rc: lp.RC,
|
||||
_arena: Allocator,
|
||||
|
||||
_x: f64,
|
||||
|
||||
@@ -28,7 +28,7 @@ pub const FilterOpts = NodeFilter.FilterOpts;
|
||||
|
||||
const DOMTreeWalker = @This();
|
||||
|
||||
_rc: lp.RC(u8) = .{},
|
||||
_rc: lp.RC = .{},
|
||||
_root: *Node,
|
||||
_what_to_show: u32,
|
||||
_filter: NodeFilter,
|
||||
|
||||
@@ -50,7 +50,7 @@ pub fn registerTypes() []const type {
|
||||
_arena: Allocator,
|
||||
// Refcounted so the GC weak-finalizer (or page teardown) releases the pooled
|
||||
// arena exactly once; mirrors Blob's lifecycle.
|
||||
_rc: lp.RC(u32) = .{},
|
||||
_rc: lp.RC = .{},
|
||||
_items: std.ArrayList(*DataTransferItem) = .{},
|
||||
_item_list: *DataTransferItemList,
|
||||
// FileList lives on the factory slab and is frame-tracked, so each File ref it
|
||||
|
||||
@@ -65,7 +65,7 @@ _time_origin: u64 = 0,
|
||||
// - 0: no reference, always a transient state going to either 1 or about to be deinit'd
|
||||
// - 1: either zig or v8 have a reference
|
||||
// - 2: both zig and v8 have a reference
|
||||
_rc: lp.RC(u8) = .{},
|
||||
_rc: lp.RC = .{},
|
||||
|
||||
pub const EventPhase = enum(u8) {
|
||||
none = 0,
|
||||
|
||||
@@ -33,7 +33,7 @@ const Allocator = std.mem.Allocator;
|
||||
/// https://developer.mozilla.org/en-US/docs/Web/API/FileReader
|
||||
const FileReader = @This();
|
||||
|
||||
_rc: lp.RC(u8) = .{},
|
||||
_rc: lp.RC = .{},
|
||||
_exec: *Execution,
|
||||
_proto: *EventTarget,
|
||||
_arena: Allocator,
|
||||
|
||||
@@ -28,7 +28,7 @@ const Execution = js.Execution;
|
||||
/// https://developer.mozilla.org/en-US/docs/Web/API/ImageData/ImageData
|
||||
const ImageData = @This();
|
||||
|
||||
_rc: lp.RC(u8) = .{},
|
||||
_rc: lp.RC = .{},
|
||||
_width: u32,
|
||||
_height: u32,
|
||||
_data: js.ArrayBufferRef(.uint8_clamped).Global,
|
||||
|
||||
@@ -39,7 +39,7 @@ pub fn registerTypes() []const type {
|
||||
|
||||
const IntersectionObserver = @This();
|
||||
|
||||
_rc: lp.RC(u8) = .{},
|
||||
_rc: lp.RC = .{},
|
||||
_arena: Allocator,
|
||||
_callback: js.Function.Global,
|
||||
_observing: std.ArrayList(*Element) = .{},
|
||||
@@ -316,7 +316,7 @@ pub fn deliverEntries(self: *IntersectionObserver, frame: *Frame) !void {
|
||||
}
|
||||
|
||||
pub const IntersectionObserverEntry = struct {
|
||||
_rc: lp.RC(u8) = .{},
|
||||
_rc: lp.RC = .{},
|
||||
_arena: Allocator,
|
||||
_time: f64,
|
||||
_target: *Element,
|
||||
|
||||
@@ -28,7 +28,7 @@ const Frame = @import("../Frame.zig");
|
||||
const Location = @This();
|
||||
|
||||
_url: *URL,
|
||||
_rc: lp.RC(u32) = .{},
|
||||
_rc: lp.RC = .{},
|
||||
|
||||
pub fn init(raw_url: []const u8, frame: *Frame) !*Location {
|
||||
const url = try URL.init(raw_url, null, &frame.js.execution);
|
||||
|
||||
@@ -39,7 +39,7 @@ pub fn registerTypes() []const type {
|
||||
|
||||
const MutationObserver = @This();
|
||||
|
||||
_rc: lp.RC(u8) = .{},
|
||||
_rc: lp.RC = .{},
|
||||
_arena: Allocator,
|
||||
_callback: js.Function.Global,
|
||||
_observing: std.ArrayList(Observing) = .{},
|
||||
@@ -357,7 +357,7 @@ pub fn deliverRecords(self: *MutationObserver, frame: *Frame) !void {
|
||||
}
|
||||
|
||||
pub const MutationRecord = struct {
|
||||
_rc: lp.RC(u8) = .{},
|
||||
_rc: lp.RC = .{},
|
||||
_type: Type,
|
||||
_target: *Node,
|
||||
_arena: Allocator,
|
||||
|
||||
@@ -29,7 +29,7 @@ const Allocator = std.mem.Allocator;
|
||||
|
||||
const Notification = @This();
|
||||
|
||||
_rc: lp.RC(u8) = .{},
|
||||
_rc: lp.RC = .{},
|
||||
_arena: Allocator,
|
||||
_proto: *EventTarget,
|
||||
_title: []const u8,
|
||||
|
||||
@@ -61,7 +61,7 @@ pub fn query(_: *const Permissions, qd: QueryDescriptor, exec: *const Execution)
|
||||
}
|
||||
|
||||
const PermissionStatus = struct {
|
||||
_rc: lp.RC(u8) = .{},
|
||||
_rc: lp.RC = .{},
|
||||
_arena: Allocator,
|
||||
_name: []const u8,
|
||||
_state: State,
|
||||
|
||||
@@ -33,7 +33,7 @@ const Selection = @This();
|
||||
|
||||
pub const SelectionDirection = enum { backward, forward, none };
|
||||
|
||||
_rc: lp.RC(u8) = .{},
|
||||
_rc: lp.RC = .{},
|
||||
_range: ?*Range = null,
|
||||
_direction: SelectionDirection = .none,
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ _url: *U.Url = undefined,
|
||||
_port: [5]u8 = undefined,
|
||||
_search_params: ?*URLSearchParams = null,
|
||||
/// We have to track lifetime of URL to free `_url`.
|
||||
_rc: lp.RC(u32) = .{},
|
||||
_rc: lp.RC = .{},
|
||||
|
||||
// convenience
|
||||
pub const resolve = @import("../URL.zig").resolve;
|
||||
|
||||
@@ -42,7 +42,7 @@ const Allocator = std.mem.Allocator;
|
||||
|
||||
const XPathExpression = @This();
|
||||
|
||||
_rc: lp.RC(u8) = .{},
|
||||
_rc: lp.RC = .{},
|
||||
_arena: Allocator,
|
||||
_expr: *const xpath.Ast.Expr,
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ const Value = union(enum) {
|
||||
nodes: []const *Node,
|
||||
};
|
||||
|
||||
_rc: lp.RC(u8) = .{},
|
||||
_rc: lp.RC = .{},
|
||||
_arena: Allocator,
|
||||
_type: u16,
|
||||
_value: Value,
|
||||
|
||||
@@ -34,7 +34,7 @@ const PlayState = enum {
|
||||
finished,
|
||||
};
|
||||
|
||||
_rc: lp.RC(u32) = .{},
|
||||
_rc: lp.RC = .{},
|
||||
_frame: *Frame,
|
||||
_arena: Allocator,
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ pub fn registerTypes() []const type {
|
||||
|
||||
pub const DOMStringList = @This();
|
||||
|
||||
_rc: lp.RC(u8) = .{},
|
||||
_rc: lp.RC = .{},
|
||||
_arena: Allocator,
|
||||
_items: []const []const u8,
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ _data: union(Mode) {
|
||||
form: NodeLive(.form),
|
||||
empty: void,
|
||||
},
|
||||
_rc: lp.RC(u32) = .{},
|
||||
_rc: lp.RC = .{},
|
||||
|
||||
pub fn deinit(self: *HTMLCollection, page: *Page) void {
|
||||
page.factory.destroy(self);
|
||||
|
||||
@@ -39,7 +39,7 @@ _data: union(enum) {
|
||||
radio_node_list: *RadioNodeList,
|
||||
name: NodeLive(.name),
|
||||
},
|
||||
_rc: lp.RC(u32) = .{},
|
||||
_rc: lp.RC = .{},
|
||||
|
||||
pub fn deinit(self: *NodeList, page: *Page) void {
|
||||
switch (self._data) {
|
||||
|
||||
@@ -27,7 +27,7 @@ pub fn Entry(comptime Inner: type, comptime field: ?[]const u8) type {
|
||||
|
||||
return struct {
|
||||
_inner: Inner,
|
||||
_rc: lp.RC(u8) = .{},
|
||||
_rc: lp.RC = .{},
|
||||
|
||||
const Self = @This();
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ const Allocator = std.mem.Allocator;
|
||||
|
||||
const FontFace = @This();
|
||||
|
||||
_rc: lp.RC(u8) = .{},
|
||||
_rc: lp.RC = .{},
|
||||
_arena: Allocator,
|
||||
_family: []const u8,
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ const Allocator = std.mem.Allocator;
|
||||
|
||||
const FontFaceSet = @This();
|
||||
|
||||
_rc: lp.RC(u8) = .{},
|
||||
_rc: lp.RC = .{},
|
||||
_proto: *EventTarget,
|
||||
_arena: Allocator,
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ const Allocator = std.mem.Allocator;
|
||||
|
||||
const TextDecoder = @This();
|
||||
|
||||
_rc: lp.RC(u8) = .{},
|
||||
_rc: lp.RC = .{},
|
||||
_fatal: bool,
|
||||
_arena: Allocator,
|
||||
_ignore_bom: bool,
|
||||
|
||||
@@ -39,7 +39,7 @@ const IS_DEBUG = @import("builtin").mode == .Debug;
|
||||
// https://html.spec.whatwg.org/multipage/server-sent-events.html
|
||||
const EventSource = @This();
|
||||
|
||||
_rc: lp.RC(u16) = .{},
|
||||
_rc: lp.RC = .{},
|
||||
_exec: *const Execution,
|
||||
_proto: *EventTarget,
|
||||
_arena: Allocator,
|
||||
|
||||
@@ -34,7 +34,7 @@ const Allocator = std.mem.Allocator;
|
||||
|
||||
const FormData = @This();
|
||||
|
||||
_rc: lp.RC(u8),
|
||||
_rc: lp.RC,
|
||||
|
||||
_arena: Allocator,
|
||||
_entries: std.ArrayList(Entry),
|
||||
|
||||
@@ -36,7 +36,7 @@ const Allocator = std.mem.Allocator;
|
||||
|
||||
const Request = @This();
|
||||
|
||||
_rc: lp.RC(u8) = .{},
|
||||
_rc: lp.RC = .{},
|
||||
_url: [:0]const u8,
|
||||
_method: http.Method,
|
||||
_headers: ?*Headers,
|
||||
|
||||
@@ -44,7 +44,7 @@ pub const Type = enum {
|
||||
opaqueredirect,
|
||||
};
|
||||
|
||||
_rc: lp.RC(u8) = .{},
|
||||
_rc: lp.RC = .{},
|
||||
_status: u16,
|
||||
_arena: Allocator,
|
||||
_headers: *Headers,
|
||||
|
||||
@@ -41,7 +41,7 @@ pub fn registerTypes() []const type {
|
||||
|
||||
const URLSearchParams = @This();
|
||||
|
||||
_rc: lp.RC(u8) = .{},
|
||||
_rc: lp.RC = .{},
|
||||
_arena: Allocator,
|
||||
_params: KeyValueList,
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ const IS_DEBUG = @import("builtin").mode == .Debug;
|
||||
|
||||
const WebSocket = @This();
|
||||
|
||||
_rc: lp.RC(u8) = .{},
|
||||
_rc: lp.RC = .{},
|
||||
_exec: *const Execution,
|
||||
_proto: *EventTarget,
|
||||
_arena: Allocator,
|
||||
|
||||
@@ -42,7 +42,7 @@ const Allocator = std.mem.Allocator;
|
||||
const IS_DEBUG = @import("builtin").mode == .Debug;
|
||||
|
||||
const XMLHttpRequest = @This();
|
||||
_rc: lp.RC(u8) = .{},
|
||||
_rc: lp.RC = .{},
|
||||
_exec: *const Execution,
|
||||
_proto: *XMLHttpRequestEventTarget,
|
||||
_upload: ?*XMLHttpRequestUpload = null,
|
||||
|
||||
@@ -59,7 +59,7 @@ _durability: Durability = .default,
|
||||
// owned object, one while a drain task is scheduled, one while parked on the
|
||||
// engine's connection gate (gate ownership needs no ref of its own: it's only
|
||||
// ever held while a drain task exists).
|
||||
_rc: lp.RC(u32) = .{},
|
||||
_rc: lp.RC = .{},
|
||||
_arena: Allocator,
|
||||
|
||||
// v8 handles owned by the transaction, swept (reset) in deinit. Slots are
|
||||
|
||||
@@ -329,42 +329,42 @@ noinline fn assertionFailure(comptime ctx: []const u8, args: anytype) noreturn {
|
||||
const rc_canary: u32 = 0x52434E54;
|
||||
const rc_poison: u32 = 0xDEADC0DE;
|
||||
|
||||
// Reference counting helper
|
||||
pub fn RC(comptime T: type) type {
|
||||
return struct {
|
||||
_refs: std.atomic.Value(T) = .init(0),
|
||||
_canary: u32 = rc_canary,
|
||||
// Reference counting helper. The count is a u32: a u8 silently wrapped at 256
|
||||
// concurrent refs (e.g. hundreds of live iterators on one URLSearchParams),
|
||||
// causing a premature deinit and a poisoned "release overflow" crash.
|
||||
pub const RC = struct {
|
||||
_refs: std.atomic.Value(u32) = .init(0),
|
||||
_canary: u32 = rc_canary,
|
||||
|
||||
pub fn init(refs: T) @This() {
|
||||
return .{ ._refs = .init(refs) };
|
||||
}
|
||||
pub fn init(refs: u32) RC {
|
||||
return .{ ._refs = .init(refs) };
|
||||
}
|
||||
|
||||
pub fn acquire(self: *@This()) void {
|
||||
_ = self._refs.fetchAdd(1, .monotonic);
|
||||
}
|
||||
pub fn acquire(self: *RC) void {
|
||||
_ = self._refs.fetchAdd(1, .monotonic);
|
||||
}
|
||||
|
||||
pub fn release(self: *@This(), value: anytype, page: *Page) void {
|
||||
const prev = self._refs.fetchSub(1, .acq_rel);
|
||||
assert(prev > 0, "release overflow", .{
|
||||
.type = @typeName(@TypeOf(value)),
|
||||
.canary = self._canary, // rc_canary=live/accounting, rc_poison=double-release, else=reuse
|
||||
.refs = prev,
|
||||
.ptr = @intFromPtr(value),
|
||||
});
|
||||
if (prev == 1) {
|
||||
// Mark dead before deinit frees this memory, so a stale
|
||||
// weak-callback re-fire reads rc_poison instead of a
|
||||
// misleadingly-intact canary.
|
||||
self._canary = rc_poison;
|
||||
value.deinit(page);
|
||||
}
|
||||
pub fn release(self: *RC, value: anytype, page: *Page) void {
|
||||
const prev = self._refs.fetchSub(1, .acq_rel);
|
||||
assert(prev > 0, "release overflow", .{
|
||||
.type = @typeName(@TypeOf(value)),
|
||||
.canary = self._canary, // rc_canary=live/accounting, rc_poison=double-release, else=reuse
|
||||
.refs = prev,
|
||||
.ptr = @intFromPtr(value),
|
||||
});
|
||||
if (prev == 1) {
|
||||
// Mark dead before deinit frees this memory, so a stale
|
||||
// weak-callback re-fire reads rc_poison instead of a
|
||||
// misleadingly-intact canary.
|
||||
self._canary = rc_poison;
|
||||
value.deinit(page);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format(self: @This(), writer: *std.Io.Writer) !void {
|
||||
return writer.print("{d}", .{self._refs.load(.monotonic)});
|
||||
}
|
||||
};
|
||||
}
|
||||
pub fn format(self: RC, writer: *std.Io.Writer) !void {
|
||||
return writer.print("{d}", .{self._refs.load(.monotonic)});
|
||||
}
|
||||
};
|
||||
|
||||
const testing = @import("testing.zig");
|
||||
test "writeJsonEnvelope: null frame" {
|
||||
|
||||
Reference in New Issue
Block a user