From fdd255c78201ee11da23661b8a2e7b5b5461b09d Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 9 Jul 2026 21:41:22 -0700 Subject: [PATCH] terminal: reserve title terminator atomically setTitle appended the title bytes and terminating NUL with separate fallible operations. If only the terminator allocation failed, it returned OutOfMemory but left a nonempty unterminated buffer that made getTitle panic on its sentinel check. Reserve checked capacity before clearing the existing title, then append both the title and terminator without further allocation. Allocation failure now leaves the prior valid title intact. --- src/terminal/Terminal.zig | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/terminal/Terminal.zig b/src/terminal/Terminal.zig index b79f21bb7..980c7225c 100644 --- a/src/terminal/Terminal.zig +++ b/src/terminal/Terminal.zig @@ -3591,11 +3591,18 @@ test "Terminal: setPwd preserves a sentinel on allocation failure" { /// Set the title for the terminal, as set by escape sequences (e.g. OSC 0/2). pub fn setTitle(self: *Terminal, t: []const u8) !void { - self.title.clearRetainingCapacity(); - if (t.len > 0) { - try self.title.appendSlice(self.gpa(), t); - try self.title.append(self.gpa(), 0); + if (t.len == 0) { + self.title.clearRetainingCapacity(); + return; } + + const capacity = std.math.add(usize, t.len, 1) catch + return error.OutOfMemory; + try self.title.ensureTotalCapacity(self.gpa(), capacity); + + self.title.clearRetainingCapacity(); + self.title.appendSliceAssumeCapacity(t); + self.title.appendAssumeCapacity(0); } /// Returns the title for the terminal, if any. The memory is owned by the @@ -3605,6 +3612,18 @@ pub fn getTitle(self: *const Terminal) ?[:0]const u8 { return self.title.items[0 .. self.title.items.len - 1 :0]; } +test "Terminal: setTitle preserves a sentinel on allocation failure" { + var failing = testing.FailingAllocator.init(testing.allocator, .{}); + const alloc = failing.allocator(); + var t = try init(alloc, .{ .cols = 5, .rows = 1 }); + defer t.deinit(alloc); + + try t.title.ensureTotalCapacityPrecise(alloc, 5); + failing.fail_index = failing.alloc_index; + try testing.expectError(error.OutOfMemory, t.setTitle("title")); + try testing.expect(t.getTitle() == null); +} + /// Switch to the given screen type (alternate or primary). /// /// This does NOT handle behaviors such as clearing the screen,