From a6ba80173805c1881c0dae2b71ac5f10cd931d5a Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Tue, 10 Feb 2026 11:30:10 +0800 Subject: [PATCH] Fix double-free of XHR on double client abort It's possible (in fact normal) for client.abort to be called twice on a schedule navigation. We immediately abort any pending requests once a secondary navigation is called (is that right?), and then again when the page shuts down. The first abort will kill the transfer, so the XHR object has to null this value so that, on context shutdown, when the finalizer is called, we don't try to free it again. --- src/browser/webapi/net/XMLHttpRequest.zig | 6 ++++++ src/lightpanda.zig | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/browser/webapi/net/XMLHttpRequest.zig b/src/browser/webapi/net/XMLHttpRequest.zig index 73e35309..cb948a19 100644 --- a/src/browser/webapi/net/XMLHttpRequest.zig +++ b/src/browser/webapi/net/XMLHttpRequest.zig @@ -233,6 +233,7 @@ pub fn send(self: *XMLHttpRequest, body_: ?[]const u8) !void { .data_callback = httpDataCallback, .done_callback = httpDoneCallback, .error_callback = httpErrorCallback, + .shutdown_callback = httpShutdownCallback, }); page.js.strongRef(self); @@ -463,6 +464,11 @@ fn httpErrorCallback(ctx: *anyopaque, err: anyerror) void { self._page.js.weakRef(self); } +fn httpShutdownCallback(ctx: *anyopaque) void { + const self: *XMLHttpRequest = @ptrCast(@alignCast(ctx)); + self._transfer = null; +} + pub fn abort(self: *XMLHttpRequest) void { self.handleError(error.Abort); if (self._transfer) |transfer| { diff --git a/src/lightpanda.zig b/src/lightpanda.zig index 59ac4dc9..c40120cc 100644 --- a/src/lightpanda.zig +++ b/src/lightpanda.zig @@ -39,9 +39,10 @@ pub const FetchOpts = struct { writer: ?*std.Io.Writer = null, }; pub fn fetch(app: *App, url: [:0]const u8, opts: FetchOpts) !void { - var browser = try Browser.init(app, .{}); const notification = try Notification.init(app.allocator); defer notification.deinit(); + + var browser = try Browser.init(app, .{}); defer browser.deinit(); var session = try browser.newSession(notification);