diff --git a/src/frame_worker.zig b/src/frame_worker.zig index ff41796..76f44a1 100644 --- a/src/frame_worker.zig +++ b/src/frame_worker.zig @@ -27,7 +27,7 @@ fn microTimestamp(io: Io) i64 { pub const FrameWork = struct { data: []u8, // raw frame bytes (heap-duped by reader, freed by worker) host_id: u64, - hostname: []const u8, // borrowed from subscriber (stable lifetime) + hostname: []const u8, // owned (heap-duped at submit, freed by worker) allocator: Allocator, io: Io, // shared references (all thread-safe, all outlive the work item) @@ -41,6 +41,7 @@ pub const FrameWork = struct { pub fn processFrame(work: *FrameWork) void { _ = work.bc.stats.pool_queued_bytes.fetchSub(work.data.len, .monotonic); defer work.allocator.free(work.data); + defer work.allocator.free(work.hostname); var arena = std.heap.ArenaAllocator.init(work.allocator); defer arena.deinit(); diff --git a/src/subscriber.zig b/src/subscriber.zig index 51ed362..bb4c1b8 100644 --- a/src/subscriber.zig +++ b/src/subscriber.zig @@ -478,11 +478,19 @@ const FrameHandler = struct { break :blk if (d) |s| std.hash.Wyhash.hash(0, s) else sub.options.host_id; }; const duped = sub.allocator.dupe(u8, data) catch return; + // dupe hostname per-frame: subscriber teardown (slurper.runWorker) + // frees sub.options.hostname after sub.run() returns, but FrameWorks + // can still be queued in the pool. borrowing the slice would be a + // use-after-free (corrupt hostnames in chain-break logs, etc.). + const hostname_dup = sub.allocator.dupe(u8, sub.options.hostname) catch { + sub.allocator.free(duped); + return; + }; const t0 = nanoTimestamp(io); if (pool.submit(did_key, .{ .data = duped, .host_id = sub.options.host_id, - .hostname = sub.options.hostname, + .hostname = hostname_dup, .allocator = sub.allocator, .io = sub.pool_io orelse sub.io, // pool_io (Threaded) for worker-safe ops .bc = sub.bc, @@ -500,6 +508,7 @@ const FrameHandler = struct { } else { // shutdown requested — don't advance cursor so reconnect replays this frame sub.allocator.free(duped); + sub.allocator.free(hostname_dup); } return; }