mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-04-06 07:38:21 +00:00
terminal: insertBlanks should not crash with count 0 and CSI @ clamps to 1 min (#11111)
CSI @ (ICH) with an explicit parameter of 0 should be clamped to 1, matching xterm behavior. Previously, a zero count reached Terminal.insertBlanks which called clearCells with an empty slice, triggering an out-of-bounds panic. Fix the stream dispatch to clamp 0 to 1 via @max, and add a defensive guard in insertBlanks for count == 0. Found by AFL++ stream fuzzer. #11109
This commit is contained in:
@@ -2166,6 +2166,12 @@ pub fn insertBlanks(self: *Terminal, count: usize) void {
|
||||
// xterm does.
|
||||
self.screens.active.cursor.pending_wrap = false;
|
||||
|
||||
// If we're given a zero then we do nothing. The rest of this function
|
||||
// assumes count > 0 and will crash if zero so return early. Note that
|
||||
// this shouldn't be possible with real CSI sequences because the value
|
||||
// is clamped to 1 min.
|
||||
if (count == 0) return;
|
||||
|
||||
// If our cursor is outside the margins then do nothing. We DO reset
|
||||
// wrap state still so this must remain below the above logic.
|
||||
if (self.screens.active.cursor.x < self.scrolling_region.left or
|
||||
@@ -9409,6 +9415,25 @@ test "Terminal: DECALN resets graphemes with protected mode" {
|
||||
}
|
||||
}
|
||||
|
||||
test "Terminal: insertBlanks zero" {
|
||||
const alloc = testing.allocator;
|
||||
var t = try init(alloc, .{ .cols = 5, .rows = 2 });
|
||||
defer t.deinit(alloc);
|
||||
|
||||
try t.print('A');
|
||||
try t.print('B');
|
||||
try t.print('C');
|
||||
t.setCursorPos(1, 1);
|
||||
|
||||
t.insertBlanks(0);
|
||||
|
||||
{
|
||||
const str = try t.plainString(testing.allocator);
|
||||
defer testing.allocator.free(str);
|
||||
try testing.expectEqualStrings("ABC", str);
|
||||
}
|
||||
}
|
||||
|
||||
test "Terminal: insertBlanks" {
|
||||
// NOTE: this is not verified with conformance tests, so these
|
||||
// tests might actually be verifying wrong behavior.
|
||||
|
||||
@@ -1894,7 +1894,7 @@ pub fn Stream(comptime Handler: type) type {
|
||||
'@' => switch (input.intermediates.len) {
|
||||
0 => try self.handler.vt(.insert_blanks, switch (input.params.len) {
|
||||
0 => 1,
|
||||
1 => input.params[0],
|
||||
1 => @max(1, input.params[0]),
|
||||
else => {
|
||||
@branchHint(.unlikely);
|
||||
log.warn("invalid ICH command: {f}", .{input});
|
||||
@@ -2966,6 +2966,28 @@ test "stream: insert characters" {
|
||||
try testing.expect(!s.handler.called);
|
||||
}
|
||||
|
||||
test "stream: insert characters explicit zero clamps to 1" {
|
||||
const H = struct {
|
||||
const Self = @This();
|
||||
value: ?usize = null,
|
||||
|
||||
pub fn vt(
|
||||
self: *Self,
|
||||
comptime action: anytype,
|
||||
value: anytype,
|
||||
) !void {
|
||||
switch (action) {
|
||||
.insert_blanks => self.value = value,
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var s: Stream(H) = .init(.{});
|
||||
for ("\x1B[0@") |c| try s.next(c);
|
||||
try testing.expectEqual(@as(usize, 1), s.handler.value.?);
|
||||
}
|
||||
|
||||
test "stream: SCOSC" {
|
||||
const H = struct {
|
||||
const Self = @This();
|
||||
|
||||
Reference in New Issue
Block a user