From d3a52ab928fed650660d36f19941d1459bc8deb0 Mon Sep 17 00:00:00 2001 From: Qwerasd Date: Wed, 12 Feb 2025 21:22:59 -0500 Subject: [PATCH] fix(termio): heap-allocate Subprocess arena so alloc doesn't get lost Previously, if `cfg.env` was `null` and we made our own `EnvMap`, it would experience a bad access when trying to deinit because the alloc it stored had a pointer to the stack allocated arena object, which would be invalid. --- src/termio/Exec.zig | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/termio/Exec.zig b/src/termio/Exec.zig index caef2229d..f8529c996 100644 --- a/src/termio/Exec.zig +++ b/src/termio/Exec.zig @@ -702,7 +702,7 @@ const Subprocess = struct { @cInclude("unistd.h"); }); - arena: std.heap.ArenaAllocator, + arena: *std.heap.ArenaAllocator, cwd: ?[]const u8, env: ?EnvMap, args: [][]const u8, @@ -718,8 +718,15 @@ const Subprocess = struct { pub fn init(gpa: Allocator, cfg: Config) !Subprocess { // We have a lot of maybe-allocations that all share the same lifetime // so use an arena so we don't end up in an accounting nightmare. - var arena = std.heap.ArenaAllocator.init(gpa); - errdefer arena.deinit(); + // + // The arena is heap-allocated because if we have our own + // env map it retains a pointer to it via the allocator. + const arena = try gpa.create(std.heap.ArenaAllocator); + arena.* = std.heap.ArenaAllocator.init(gpa); + errdefer { + arena.deinit(); + gpa.destroy(arena); + } const alloc = arena.allocator(); // Get our env. If a default env isn't provided by the caller @@ -1055,7 +1062,9 @@ const Subprocess = struct { self.stop(); if (self.pty) |*pty| pty.deinit(); if (self.env) |*env| env.deinit(); + const arena_alloc = self.arena.child_allocator; self.arena.deinit(); + arena_alloc.destroy(self.arena); self.* = undefined; }