From 91f0cf67d51c4bf8c642ed1545583421764ee130 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 9 Jul 2026 21:45:15 -0700 Subject: [PATCH] terminal: preserve tabstops on resize failure Terminal.resize deinitialized the current tab stops before allocating their replacement. If a resize beyond the inline tab-stop capacity ran out of memory, the terminal retained an undefined tab-stop value and normal deinitialization dereferenced a poisoned pointer. Allocate and initialize the replacement first, then release the old tab stops only after allocation succeeds. A failed resize now retains the original tab stops and remains safe to destroy. --- src/terminal/Terminal.zig | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/terminal/Terminal.zig b/src/terminal/Terminal.zig index 980c7225c..0880ee975 100644 --- a/src/terminal/Terminal.zig +++ b/src/terminal/Terminal.zig @@ -3518,8 +3518,13 @@ pub fn resize( // Resize our tabstops if (self.cols != cols) { + const tabstops: Tabstops = try .init( + alloc, + cols, + TABSTOP_INTERVAL, + ); self.tabstops.deinit(alloc); - self.tabstops = try .init(alloc, cols, 8); + self.tabstops = tabstops; } // Resize primary screen, which supports reflow @@ -3554,6 +3559,19 @@ pub fn resize( }; } +test "Terminal: resize preserves tabstops on allocation failure" { + var failing = testing.FailingAllocator.init(testing.allocator, .{}); + const alloc = failing.allocator(); + var t = try init(alloc, .{ .cols = 10, .rows = 1 }); + defer t.deinit(alloc); + + failing.fail_index = failing.alloc_index; + try testing.expectError(error.OutOfMemory, t.resize(alloc, 513, 1)); + + try testing.expectEqual(@as(size.CellCountInt, 10), t.cols); + try testing.expect(t.tabstops.get(8)); +} + /// Set the pwd for the terminal. pub fn setPwd(self: *Terminal, pwd: []const u8) !void { if (pwd.len == 0) {