Merge pull request #1846 from lightpanda-io/origin_cdp_fix

Fix use-after-free with certain CDP scripts
This commit is contained in:
Karl Seguin
2026-03-17 06:43:07 +08:00
committed by GitHub
2 changed files with 32 additions and 24 deletions

View File

@@ -167,12 +167,11 @@ pub fn setOrigin(self: *Context, key: ?[]const u8) !void {
const env = self.env; const env = self.env;
const isolate = env.isolate; const isolate = env.isolate;
lp.assert(self.origin.rc == 1, "Ref opaque origin", .{ .rc = self.origin.rc });
const origin = try self.session.getOrCreateOrigin(key); const origin = try self.session.getOrCreateOrigin(key);
errdefer self.session.releaseOrigin(origin); errdefer self.session.releaseOrigin(origin);
try origin.takeover(self.origin);
try self.origin.transferTo(origin);
lp.assert(self.origin.rc == 1, "Ref opaque origin", .{ .rc = self.origin.rc });
self.origin.deinit(env.app);
self.origin = origin; self.origin = origin;

View File

@@ -68,6 +68,8 @@ temps: std.AutoHashMapUnmanaged(usize, v8.Global) = .empty,
// if v8 hasn't called the finalizer directly itself. // if v8 hasn't called the finalizer directly itself.
finalizer_callbacks: std.AutoHashMapUnmanaged(usize, *FinalizerCallback) = .empty, finalizer_callbacks: std.AutoHashMapUnmanaged(usize, *FinalizerCallback) = .empty,
taken_over: std.ArrayList(*Origin),
pub fn init(app: *App, isolate: js.Isolate, key: []const u8) !*Origin { pub fn init(app: *App, isolate: js.Isolate, key: []const u8) !*Origin {
const arena = try app.arena_pool.acquire(); const arena = try app.arena_pool.acquire();
errdefer app.arena_pool.release(arena); errdefer app.arena_pool.release(arena);
@@ -86,14 +88,19 @@ pub fn init(app: *App, isolate: js.Isolate, key: []const u8) !*Origin {
.rc = 1, .rc = 1,
.arena = arena, .arena = arena,
.key = owned_key, .key = owned_key,
.globals = .empty,
.temps = .empty, .temps = .empty,
.globals = .empty,
.taken_over = .empty,
.security_token = token_global, .security_token = token_global,
}; };
return self; return self;
} }
pub fn deinit(self: *Origin, app: *App) void { pub fn deinit(self: *Origin, app: *App) void {
for (self.taken_over.items) |o| {
o.deinit(app);
}
// Call finalizers before releasing anything // Call finalizers before releasing anything
{ {
var it = self.finalizer_callbacks.valueIterator(); var it = self.finalizer_callbacks.valueIterator();
@@ -196,42 +203,44 @@ pub fn createFinalizerCallback(
return fc; return fc;
} }
pub fn transferTo(self: *Origin, dest: *Origin) !void { pub fn takeover(self: *Origin, original: *Origin) !void {
const arena = dest.arena; const arena = self.arena;
try dest.globals.ensureUnusedCapacity(arena, self.globals.items.len); try self.globals.ensureUnusedCapacity(arena, self.globals.items.len);
for (self.globals.items) |obj| { for (original.globals.items) |obj| {
dest.globals.appendAssumeCapacity(obj); self.globals.appendAssumeCapacity(obj);
} }
self.globals.clearRetainingCapacity(); original.globals.clearRetainingCapacity();
{ {
try dest.temps.ensureUnusedCapacity(arena, self.temps.count()); try self.temps.ensureUnusedCapacity(arena, original.temps.count());
var it = self.temps.iterator(); var it = original.temps.iterator();
while (it.next()) |kv| { while (it.next()) |kv| {
try dest.temps.put(arena, kv.key_ptr.*, kv.value_ptr.*); try self.temps.put(arena, kv.key_ptr.*, kv.value_ptr.*);
} }
self.temps.clearRetainingCapacity(); original.temps.clearRetainingCapacity();
} }
{ {
try dest.finalizer_callbacks.ensureUnusedCapacity(arena, self.finalizer_callbacks.count()); try self.finalizer_callbacks.ensureUnusedCapacity(arena, original.finalizer_callbacks.count());
var it = self.finalizer_callbacks.iterator(); var it = original.finalizer_callbacks.iterator();
while (it.next()) |kv| { while (it.next()) |kv| {
kv.value_ptr.*.origin = dest; kv.value_ptr.*.origin = self;
try dest.finalizer_callbacks.put(arena, kv.key_ptr.*, kv.value_ptr.*); try self.finalizer_callbacks.put(arena, kv.key_ptr.*, kv.value_ptr.*);
} }
self.finalizer_callbacks.clearRetainingCapacity(); original.finalizer_callbacks.clearRetainingCapacity();
} }
{ {
try dest.identity_map.ensureUnusedCapacity(arena, self.identity_map.count()); try self.identity_map.ensureUnusedCapacity(arena, original.identity_map.count());
var it = self.identity_map.iterator(); var it = original.identity_map.iterator();
while (it.next()) |kv| { while (it.next()) |kv| {
try dest.identity_map.put(arena, kv.key_ptr.*, kv.value_ptr.*); try self.identity_map.put(arena, kv.key_ptr.*, kv.value_ptr.*);
} }
self.identity_map.clearRetainingCapacity(); original.identity_map.clearRetainingCapacity();
} }
try self.taken_over.append(self.arena, original);
} }
// A type that has a finalizer can have its finalizer called one of two ways. // A type that has a finalizer can have its finalizer called one of two ways.