Merge pull request #724 from mitchellh/fuzz

Fix a couple crashes found while fuzzing
This commit is contained in:
Mitchell Hashimoto
2023-10-24 09:52:36 -07:00
committed by GitHub
3 changed files with 132 additions and 13 deletions

View File

@ -1413,6 +1413,25 @@ pub fn cursorLeft(self: *Terminal, count_req: usize) void {
else else
self.scrolling_region.left; self.scrolling_region.left;
// Handle some edge cases when our cursor is already on the left margin.
if (self.screen.cursor.x == left_margin) {
switch (wrap_mode) {
// In reverse mode, if we're already before the top margin
// then we just set our cursor to the top-left and we're done.
.reverse => if (self.screen.cursor.y <= top) {
self.screen.cursor.x = left_margin;
self.screen.cursor.y = top;
return;
},
// Handled in while loop
.reverse_extended => {},
// Handled above
.none => unreachable,
}
}
while (true) { while (true) {
// We can move at most to the left margin. // We can move at most to the left margin.
const max = self.screen.cursor.x - left_margin; const max = self.screen.cursor.x - left_margin;
@ -1437,8 +1456,10 @@ pub fn cursorLeft(self: *Terminal, count_req: usize) void {
} }
// If our previous line is not wrapped then we are done. // If our previous line is not wrapped then we are done.
if (wrap_mode != .reverse_extended) {
const row = self.screen.getRow(.{ .active = self.screen.cursor.y - 1 }); const row = self.screen.getRow(.{ .active = self.screen.cursor.y - 1 });
if (wrap_mode != .reverse_extended and !row.isWrapped()) break; if (!row.isWrapped()) break;
}
self.screen.cursor.y -= 1; self.screen.cursor.y -= 1;
self.screen.cursor.x = right_margin; self.screen.cursor.x = right_margin;
count -= 1; count -= 1;
@ -1541,7 +1562,7 @@ pub fn horizontalTabBack(self: *Terminal) !void {
while (true) { while (true) {
// If we're already at the edge of the screen, then we're done. // If we're already at the edge of the screen, then we're done.
if (self.screen.cursor.x == left_limit) return; if (self.screen.cursor.x <= left_limit) return;
// Move the cursor left // Move the cursor left
self.screen.cursor.x -= 1; self.screen.cursor.x -= 1;
@ -1634,7 +1655,7 @@ pub fn insertBlanks(self: *Terminal, count: usize) void {
// Determine our indexes. // Determine our indexes.
const start = self.screen.cursor.x; const start = self.screen.cursor.x;
const pivot = self.screen.cursor.x + count; const pivot = @min(self.screen.cursor.x + count, right_limit);
// This is the number of spaces we have left to shift existing data. // This is the number of spaces we have left to shift existing data.
// If count is bigger than the available space left after the cursor, // If count is bigger than the available space left after the cursor,
@ -2733,6 +2754,26 @@ test "Terminal: horizontal tabs with left margin in origin mode" {
} }
} }
test "Terminal: horizontal tab back with cursor before left margin" {
const alloc = testing.allocator;
var t = try init(alloc, 20, 5);
defer t.deinit(alloc);
t.modes.set(.origin, true);
t.saveCursor();
t.modes.set(.enable_left_and_right_margin, true);
t.setLeftAndRightMargin(5, 0);
t.restoreCursor();
try t.horizontalTabBack();
try t.print('X');
{
var str = try t.plainString(testing.allocator);
defer testing.allocator.free(str);
try testing.expectEqualStrings("X", str);
}
}
test "Terminal: cursorPos resets wrap" { test "Terminal: cursorPos resets wrap" {
const alloc = testing.allocator; const alloc = testing.allocator;
var t = try init(alloc, 5, 5); var t = try init(alloc, 5, 5);
@ -4248,6 +4289,25 @@ test "Terminal: insertBlanks outside left/right scroll region" {
} }
} }
test "Terminal: insertBlanks left/right scroll region large count" {
const alloc = testing.allocator;
var t = try init(alloc, 10, 10);
defer t.deinit(alloc);
t.modes.set(.origin, true);
t.modes.set(.enable_left_and_right_margin, true);
t.setLeftAndRightMargin(3, 5);
t.setCursorPos(1, 1);
t.insertBlanks(140);
try t.print('X');
{
var str = try t.plainString(testing.allocator);
defer testing.allocator.free(str);
try testing.expectEqualStrings(" X", str);
}
}
test "Terminal: insert mode with space" { test "Terminal: insert mode with space" {
const alloc = testing.allocator; const alloc = testing.allocator;
var t = try init(alloc, 10, 2); var t = try init(alloc, 10, 2);
@ -5914,6 +5974,24 @@ test "Terminal: cursorLeft reverse wrap with no soft wrap" {
} }
} }
test "Terminal: cursorLeft reverse wrap before left margin" {
const alloc = testing.allocator;
var t = try init(alloc, 5, 5);
defer t.deinit(alloc);
t.modes.set(.wraparound, true);
t.modes.set(.reverse_wrap, true);
t.setTopAndBottomMargin(3, 0);
t.cursorLeft(1);
try t.print('X');
{
var str = try t.plainString(testing.allocator);
defer testing.allocator.free(str);
try testing.expectEqualStrings("\n\nX", str);
}
}
test "Terminal: cursorLeft extended reverse wrap" { test "Terminal: cursorLeft extended reverse wrap" {
const alloc = testing.allocator; const alloc = testing.allocator;
var t = try init(alloc, 5, 5); var t = try init(alloc, 5, 5);

View File

@ -926,13 +926,19 @@ pub fn Stream(comptime Handler: type) type {
}, },
// ICH - Insert Blanks // ICH - Insert Blanks
// TODO: test '@' => switch (action.intermediates.len) {
'@' => if (@hasDecl(T, "insertBlanks")) switch (action.params.len) { 0 => if (@hasDecl(T, "insertBlanks")) switch (action.params.len) {
0 => try self.handler.insertBlanks(1), 0 => try self.handler.insertBlanks(1),
1 => try self.handler.insertBlanks(action.params[0]), 1 => try self.handler.insertBlanks(action.params[0]),
else => log.warn("invalid ICH command: {}", .{action}), else => log.warn("invalid ICH command: {}", .{action}),
} else log.warn("unimplemented CSI callback: {}", .{action}), } else log.warn("unimplemented CSI callback: {}", .{action}),
else => log.warn(
"ignoring unimplemented CSI @: {}",
.{action},
),
},
// DECSASD - Select Active Status Display // DECSASD - Select Active Status Display
'}' => { '}' => {
const success = decsasd: { const success = decsasd: {
@ -1572,3 +1578,23 @@ test "stream: XTSHIFTESCAPE" {
try s.nextSlice("\x1B[>1s"); try s.nextSlice("\x1B[>1s");
try testing.expect(s.handler.escape.? == true); try testing.expect(s.handler.escape.? == true);
} }
test "stream: insert characters" {
const H = struct {
const Self = @This();
called: bool = false,
pub fn insertBlanks(self: *Self, v: u16) !void {
_ = v;
self.called = true;
}
};
var s: Stream(H) = .{ .handler = .{} };
for ("\x1B[42@") |c| try s.next(c);
try testing.expect(s.handler.called);
s.handler.called = false;
for ("\x1B[?42@") |c| try s.next(c);
try testing.expect(!s.handler.called);
}

View File

@ -12,8 +12,7 @@ or equal to 0, adjust `n` to be 1. If `n` is omitted, `n` defaults to 1.
This sequence always unsets the pending wrap state. This sequence always unsets the pending wrap state.
The leftmost boundary the cursor can move to is determined by the current The leftmost boundary the cursor can move to is determined by the current
cursor column and the [left margin](#TODO). If the cursor begins to the left cursor column and the [left margin](#TODO). If the cursor begins to the left of the left margin, modify the left margin to be the leftmost column
of the left margin, modify the left margin to be the leftmost column
for the duration of the sequence. The leftmost column the cursor can be on for the duration of the sequence. The leftmost column the cursor can be on
is the left margin. is the left margin.
@ -142,5 +141,21 @@ printf "X"
|A_________| |A_________|
|B_________| |B_________|
|_________Xc |_________Xc
``g ```
### CUB V-6: Reverse Wrap Outside of Margins
```bash
printf "\033[1;1H"
printf "\033[0J"
printf "\033[?45h"
printf "\033[3r"
printf "\b"
printf "X"
```
```
|__________|
|__________|
|Xc________|
``` ```