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.
This commit is contained in:
Qwerasd
2025-02-12 21:22:59 -05:00
parent 432beac315
commit d3a52ab928

View File

@ -702,7 +702,7 @@ const Subprocess = struct {
@cInclude("unistd.h"); @cInclude("unistd.h");
}); });
arena: std.heap.ArenaAllocator, arena: *std.heap.ArenaAllocator,
cwd: ?[]const u8, cwd: ?[]const u8,
env: ?EnvMap, env: ?EnvMap,
args: [][]const u8, args: [][]const u8,
@ -718,8 +718,15 @@ const Subprocess = struct {
pub fn init(gpa: Allocator, cfg: Config) !Subprocess { pub fn init(gpa: Allocator, cfg: Config) !Subprocess {
// We have a lot of maybe-allocations that all share the same lifetime // 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. // 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(); const alloc = arena.allocator();
// Get our env. If a default env isn't provided by the caller // Get our env. If a default env isn't provided by the caller
@ -1055,7 +1062,9 @@ const Subprocess = struct {
self.stop(); self.stop();
if (self.pty) |*pty| pty.deinit(); if (self.pty) |*pty| pty.deinit();
if (self.env) |*env| env.deinit(); if (self.env) |*env| env.deinit();
const arena_alloc = self.arena.child_allocator;
self.arena.deinit(); self.arena.deinit();
arena_alloc.destroy(self.arena);
self.* = undefined; self.* = undefined;
} }