Merge pull request #742 from mitchellh/fuzz-crashes

Fix more fuzz crashes
This commit is contained in:
Mitchell Hashimoto
2023-10-27 09:46:10 -07:00
committed by GitHub
4 changed files with 93 additions and 11 deletions

View File

@ -159,7 +159,10 @@ pub const Action = union(enum) {
const value = @field(self, u_field.name); const value = @field(self, u_field.name);
switch (@TypeOf(value)) { switch (@TypeOf(value)) {
// Unicode // Unicode
u21 => try std.fmt.format(writer, "'{u}'", .{value}), u21 => try std.fmt.format(writer, "'{u}' (U+{X})", .{ value, value }),
// Byte
u8 => try std.fmt.format(writer, "0x{x}", .{value}),
// Note: we don't do ASCII (u8) because there are a lot // Note: we don't do ASCII (u8) because there are a lot
// of invisible characters we don't want to handle right // of invisible characters we don't want to handle right

View File

@ -443,8 +443,8 @@ pub fn restoreCursor(self: *Terminal) void {
self.screen.cursor.pen = saved.pen; self.screen.cursor.pen = saved.pen;
self.screen.charset = saved.charset; self.screen.charset = saved.charset;
self.modes.set(.origin, saved.origin); self.modes.set(.origin, saved.origin);
self.screen.cursor.x = saved.x; self.screen.cursor.x = @min(saved.x, self.cols - 1);
self.screen.cursor.y = saved.y; self.screen.cursor.y = @min(saved.y, self.rows - 1);
self.screen.cursor.pending_wrap = saved.pending_wrap; self.screen.cursor.pending_wrap = saved.pending_wrap;
} }
@ -766,6 +766,11 @@ pub fn print(self: *Terminal, c: u21) !void {
// Attach zero-width characters to our cell as grapheme data. // Attach zero-width characters to our cell as grapheme data.
if (width == 0) { if (width == 0) {
// If we have grapheme clustering enabled, we don't blindly attach
// any zero width character to our cells and we instead just ignore
// it.
if (self.modes.get(.grapheme_cluster)) return;
// If we're at cell zero, then this is malformed data and we don't // If we're at cell zero, then this is malformed data and we don't
// print anything or even store this. Zero-width characters are ALWAYS // print anything or even store this. Zero-width characters are ALWAYS
// attached to some other non-zero-width character at the time of // attached to some other non-zero-width character at the time of
@ -883,10 +888,10 @@ fn printCell(self: *Terminal, unmapped_c: u21) *Screen.Cell {
// single-width characters into that. // single-width characters into that.
if (cell.attrs.wide) { if (cell.attrs.wide) {
const x = self.screen.cursor.x + 1; const x = self.screen.cursor.x + 1;
assert(x < self.cols); if (x < self.cols) {
const spacer_cell = row.getCellPtr(x); const spacer_cell = row.getCellPtr(x);
spacer_cell.* = self.screen.cursor.pen; spacer_cell.* = self.screen.cursor.pen;
}
if (self.screen.cursor.y > 0 and self.screen.cursor.x <= 1) { if (self.screen.cursor.y > 0 and self.screen.cursor.x <= 1) {
self.clearWideSpacerHead(); self.clearWideSpacerHead();
@ -1341,10 +1346,12 @@ pub fn deleteChars(self: *Terminal, count: usize) !void {
} }
} }
pub fn eraseChars(self: *Terminal, count: usize) void { pub fn eraseChars(self: *Terminal, count_req: usize) void {
const tracy = trace(@src()); const tracy = trace(@src());
defer tracy.end(); defer tracy.end();
const count = @max(count_req, 1);
// This resets the pending wrap state // This resets the pending wrap state
self.screen.cursor.pending_wrap = false; self.screen.cursor.pending_wrap = false;
@ -2170,6 +2177,31 @@ test "Terminal: print over wide spacer tail" {
} }
} }
test "Terminal: zero width chars with grapheme clustering can be put in their own cell" {
var t = try init(testing.allocator, 5, 5);
defer t.deinit(testing.allocator);
// Enable grapheme clustering
t.modes.set(.grapheme_cluster, true);
try t.print('x');
try t.print(0x7F); // zero-width control character
{
var str = try t.plainString(testing.allocator);
defer testing.allocator.free(str);
try testing.expectEqualStrings("x", str);
}
const row = t.screen.getRow(.{ .screen = 0 });
{
const cell = row.getCell(0);
try testing.expectEqual(@as(u32, 'x'), cell.char);
try testing.expect(!cell.attrs.wide);
try testing.expect(!cell.attrs.grapheme);
}
}
test "Terminal: VS15 to make narrow character" { test "Terminal: VS15 to make narrow character" {
var t = try init(testing.allocator, 5, 5); var t = try init(testing.allocator, 5, 5);
defer t.deinit(testing.allocator); defer t.deinit(testing.allocator);
@ -4757,6 +4789,23 @@ test "Terminal: eraseChars simple operation" {
} }
} }
test "Terminal: eraseChars minimum one" {
const alloc = testing.allocator;
var t = try init(alloc, 5, 5);
defer t.deinit(alloc);
for ("ABC") |c| try t.print(c);
t.setCursorPos(1, 1);
t.eraseChars(0);
try t.print('X');
{
var str = try t.plainString(testing.allocator);
defer testing.allocator.free(str);
try testing.expectEqualStrings("XBC", str);
}
}
test "Terminal: eraseChars beyond screen edge" { test "Terminal: eraseChars beyond screen edge" {
const alloc = testing.allocator; const alloc = testing.allocator;
var t = try init(alloc, 5, 5); var t = try init(alloc, 5, 5);
@ -5016,6 +5065,24 @@ test "Terminal: saveCursor origin mode" {
} }
} }
test "Terminal: saveCursor resize" {
const alloc = testing.allocator;
var t = try init(alloc, 10, 5);
defer t.deinit(alloc);
t.setCursorPos(1, 10);
t.saveCursor();
try t.resize(alloc, 5, 5);
t.restoreCursor();
try t.print('X');
{
var str = try t.plainString(testing.allocator);
defer testing.allocator.free(str);
try testing.expectEqualStrings(" X", str);
}
}
test "Terminal: setProtectedMode" { test "Terminal: setProtectedMode" {
const alloc = testing.allocator; const alloc = testing.allocator;
var t = try init(alloc, 3, 3); var t = try init(alloc, 3, 3);

View File

@ -197,7 +197,7 @@ pub const Parser = struct {
.b = @truncate(rgb[2]), .b = @truncate(rgb[2]),
}, },
}; };
} else if (slice.len >= 2 and slice[1] == 5) { } else if (slice.len >= 3 and slice[1] == 5) {
self.idx += 2; self.idx += 2;
return Attribute{ return Attribute{
.@"256_fg" = @truncate(slice[2]), .@"256_fg" = @truncate(slice[2]),
@ -225,7 +225,7 @@ pub const Parser = struct {
.b = @truncate(rgb[2]), .b = @truncate(rgb[2]),
}, },
}; };
} else if (slice.len >= 2 and slice[1] == 5) { } else if (slice.len >= 3 and slice[1] == 5) {
self.idx += 2; self.idx += 2;
return Attribute{ return Attribute{
.@"256_bg" = @truncate(slice[2]), .@"256_bg" = @truncate(slice[2]),
@ -532,3 +532,15 @@ test "sgr: underline, bg, and fg" {
try testing.expectEqual(Attribute.Underline.single, v.underline); try testing.expectEqual(Attribute.Underline.single, v.underline);
} }
} }
test "sgr: direct color fg missing color" {
// This used to crash
var p: Parser = .{ .params = &[_]u16{ 38, 5 }, .colon = false };
while (p.next()) |_| {}
}
test "sgr: direct color bg missing color" {
// This used to crash
var p: Parser = .{ .params = &[_]u16{ 48, 5 }, .colon = false };
while (p.next()) |_| {}
}

View File

@ -154,7 +154,7 @@ pub fn Stream(comptime Handler: type) type {
else else
log.warn("unimplemented invokeCharset: {x}", .{c}), log.warn("unimplemented invokeCharset: {x}", .{c}),
else => log.warn("invalid C0 character, ignoring: {x}", .{c}), else => log.warn("invalid C0 character, ignoring: 0x{x}", .{c}),
} }
} }