From a09088ebffd0d0b537654314d73cfce1f2372411 Mon Sep 17 00:00:00 2001 From: Emil Lerch Date: Wed, 22 Jul 2026 06:22:59 -0700 Subject: [PATCH] use response arena rather than db allocator for responses --- src/main.zig | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/main.zig b/src/main.zig index 31c0476..4eb2222 100644 --- a/src/main.zig +++ b/src/main.zig @@ -130,7 +130,7 @@ pub fn main() !u8 { return 0; } -fn indexHandler(db: *root.NotmuchDb, _: *httpz.Request, res: *httpz.Response) !void { +fn indexHandler(_: *root.NotmuchDb, _: *httpz.Request, res: *httpz.Response) !void { const file = std.fs.cwd().openFile("static/index.html", .{}) catch { res.status = 500; res.body = "Error loading index.html"; @@ -138,7 +138,7 @@ fn indexHandler(db: *root.NotmuchDb, _: *httpz.Request, res: *httpz.Response) !v }; defer file.close(); - const content = file.readToEndAlloc(db.allocator, 1024 * 1024) catch { + const content = file.readToEndAlloc(res.arena, 1024 * 1024) catch { res.status = 500; res.body = "Error reading index.html"; return; @@ -149,7 +149,7 @@ fn indexHandler(db: *root.NotmuchDb, _: *httpz.Request, res: *httpz.Response) !v res.body = content; } -fn staticHandler(db: *root.NotmuchDb, req: *httpz.Request, res: *httpz.Response) !void { +fn staticHandler(_: *root.NotmuchDb, req: *httpz.Request, res: *httpz.Response) !void { const path = req.url.path; const file_path = if (std.mem.eql(u8, path, "/style.css")) @@ -169,7 +169,7 @@ fn staticHandler(db: *root.NotmuchDb, req: *httpz.Request, res: *httpz.Response) }; defer file.close(); - const content = file.readToEndAlloc(db.allocator, 1024 * 1024) catch { + const content = file.readToEndAlloc(res.arena, 1024 * 1024) catch { res.status = 500; res.body = "Error reading file"; return; @@ -345,8 +345,13 @@ fn attachmentHandler(db: *root.NotmuchDb, req: *httpz.Request, res: *httpz.Respo } const att = msg.attachments[num]; - res.header("Content-Type", att.content_type); - res.header("Content-Disposition", try std.fmt.allocPrint(db.allocator, "attachment; filename=\"{s}\"", .{att.filename})); + // httpz stores header values by reference and serializes them after this + // handler returns -- i.e. after `defer msg.deinit` has freed att.* and + // without ever freeing anything allocated here. Copy both values into the + // per-request arena (reset by httpz after the response is written) to avoid + // a use-after-free on Content-Type and a leak on Content-Disposition. + res.header("Content-Type", try res.arena.dupe(u8, att.content_type)); + res.header("Content-Disposition", try std.fmt.allocPrint(res.arena, "attachment; filename=\"{s}\"", .{att.filename})); // TODO: Actually retrieve and send attachment content // For now, just send metadata