Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fuzzing Crashes #723

Closed
truemedian opened this issue Oct 24, 2023 · 12 comments
Closed

Fuzzing Crashes #723

truemedian opened this issue Oct 24, 2023 · 12 comments
Labels
contributor friendly A well-scoped, approachable issue for someone looking to contributor. crash Causes a crash, very high priority to fix

Comments

@truemedian
Copy link
Collaborator

truemedian commented Oct 24, 2023

The following results were obtained via fuzzing, as such they're mostly malicious towards ghostty and will not make much sense.

terminal/Screen

toScreen

Failing assert(y < RowIndexTag.screen.maxLen(screen))

via enable_mode_3

Traceback

thread 00000000 panic: reached unreachable code
std/debug.zig:342:14: 0x2481cc in assert (fuzz)
    if (!ok) unreachable; // assertion failure
             ^
src/terminal/Screen.zig:747:53: 0x2565e7 in toScreen (fuzz)
                if (std.debug.runtime_safety) assert(y < RowIndexTag.screen.maxLen(screen));
                                                    ^
src/terminal/Screen.zig:1107:34: 0x24731f in getRow (fuzz)
    const offset = index.toScreen(self).screen * (self.cols + 1);
                                 ^
src/terminal/Screen.zig:2627:41: 0x2595c8 in resize (fuzz)
                cur_old_row = old.getRow(.{ .screen = old_y });
                                        ^
src/terminal/Terminal.zig:327:31: 0x25a8d0 in resize (fuzz)
        try self.screen.resize(rows, cols);
                              ^
fuzz.zig:410:51: 0x25bf6c in setMode (fuzz)
            .enable_mode_3 => self.terminal.resize(
                                                  ^
src/terminal/stream.zig:530:53: 0x266225 in csiDispatch (fuzz)
                            try self.handler.setMode(mode, true);
                                                    ^
src/terminal/stream.zig:68:71: 0x271213 in next (fuzz)
                    .csi_dispatch => |csi_action| try self.csiDispatch(csi_action),
                                                                      ^
fuzz.zig:54:16: 0x242f48 in fuzz_next (fuzz)
    stream.next(c) catch return true;
               ^

Input

mz8zaBtbPzQwaBtjmz82OWgbWzszczAbWzEwMGUwMDAbWz80MGg= (base64)

�?3h�[?40h�c�?69h�[;3s0�[100e000�[?40h
via deccolm

Traceback

thread 00000000 panic: reached unreachable code
std/debug.zig:342:14: 0x2481cc in assert (fuzz)
    if (!ok) unreachable; // assertion failure
             ^
src/terminal/Screen.zig:747:53: 0x2565e7 in toScreen (fuzz)
                if (std.debug.runtime_safety) assert(y < RowIndexTag.screen.maxLen(screen));
                                                    ^
src/terminal/Screen.zig:1107:34: 0x24731f in getRow (fuzz)
    const offset = index.toScreen(self).screen * (self.cols + 1);
                                 ^
src/terminal/Screen.zig:2627:41: 0x2595c8 in resize (fuzz)
                cur_old_row = old.getRow(.{ .screen = old_y });
                                        ^
src/terminal/Terminal.zig:327:31: 0x25a8d0 in resize (fuzz)
        try self.screen.resize(rows, cols);
                              ^
src/terminal/Terminal.zig:292:20: 0x25b0d9 in deccolm (fuzz)
    try self.resize(alloc, 0, self.rows);
                   ^
fuzz.zig:418:56: 0x25c004 in setMode (fuzz)
            .@"132_column" => try self.terminal.deccolm(
                                                       ^
src/terminal/stream.zig:550:53: 0x266453 in csiDispatch (fuzz)
                            try self.handler.setMode(mode, false);
                                                    ^
src/terminal/stream.zig:68:71: 0x271213 in next (fuzz)
                    .csi_dispatch => |csi_action| try self.csiDispatch(csi_action),
                                                                      ^
fuzz.zig:54:16: 0x242f48 in fuzz_next (fuzz)
    stream.next(c) catch return true;
               ^

Input

mz8zaBtbPzQwaJs7NHIwmzMwZJs1MDAwYps/M2w= (base64)

�?3h�[?40h�;4r0�30d�5000b�?3l
via restoreMode

Traceback

thread 00000000 panic: reached unreachable code
std/debug.zig:342:14: 0x2481cc in assert (fuzz)
    if (!ok) unreachable; // assertion failure
             ^
src/terminal/Screen.zig:747:53: 0x2565e7 in toScreen (fuzz)
                if (std.debug.runtime_safety) assert(y < RowIndexTag.screen.maxLen(screen));
                                                    ^
src/terminal/Screen.zig:1107:34: 0x24731f in getRow (fuzz)
    const offset = index.toScreen(self).screen * (self.cols + 1);
                                 ^
src/terminal/Screen.zig:2627:41: 0x2595c8 in resize (fuzz)
                cur_old_row = old.getRow(.{ .screen = old_y });
                                        ^
src/terminal/Terminal.zig:327:31: 0x25a8d0 in resize (fuzz)
        try self.screen.resize(rows, cols);
                              ^
fuzz.zig:410:51: 0x25bf6c in setMode (fuzz)
            .enable_mode_3 => self.terminal.resize(
                                                  ^
fuzz.zig:368:25: 0x2616b0 in restoreMode (fuzz)
        try self.setMode(mode, v);
                        ^
src/terminal/stream.zig:772:65: 0x267573 in csiDispatch (fuzz)
                                    try self.handler.restoreMode(mode);
                                                                ^
src/terminal/stream.zig:68:71: 0x271213 in next (fuzz)
                    .csi_dispatch => |csi_action| try self.csiDispatch(csi_action),
                                                                      ^
fuzz.zig:54:16: 0x242f48 in fuzz_next (fuzz)
    stream.next(c) catch return true;
               ^

Input

mz82OWibPzNoMBtbPzQwaJsxN3ObNTg4MGIbWz80MHI= (base64)

�?69h�?3h0�[?40h�17s�5880b�[?40r

terminal/Terminal

print

Failing assert(!utf8proc.graphemeBreakStateful(

Traceback

thread 00000000 panic: reached unreachable code
std/debug.zig:342:14: 0x2481cc in assert (fuzz)
    if (!ok) unreachable; // assertion failure
             ^
src/terminal/Terminal.zig:682:27: 0x24e637 in print (fuzz)
                    assert(!utf8proc.graphemeBreakStateful(
                          ^
fuzz.zig:183:32: 0x251652 in print (fuzz)
        try self.terminal.print(ch);
                               ^
src/terminal/stream.zig:66:83: 0x271118 in next (fuzz)
                    .print => |p| if (@hasDecl(T, "print")) try self.handler.print(p),
                                                                                  ^
fuzz.zig:54:16: 0x242f48 in fuzz_next (fuzz)
    stream.next(c) catch return true;
               ^

Input

MAwbWz8yMDI3aOC9vdYw (base64)

0�[?2027hཽ�0

Failing assert(x < self.cols)

maybe widechar related

Traceback

thread 00000000 panic: reached unreachable code
std/debug.zig:342:14: 0x2481cc in assert (fuzz)
    if (!ok) unreachable; // assertion failure
             ^
src/terminal/Terminal.zig:881:15: 0x24f372 in print (fuzz)
        assert(x < self.cols);
              ^
fuzz.zig:183:32: 0x251652 in print (fuzz)
        try self.terminal.print(ch);
                               ^
src/terminal/stream.zig:66:83: 0x271118 in next (fuzz)
                    .print => |p| if (@hasDecl(T, "print")) try self.handler.print(p),
                                                                                  ^
fuzz.zig:54:16: 0x242f48 in fuzz_next (fuzz)
    stream.next(c) catch return true;
               ^

Input

MDAwMDAwMDAwMDAwmzRoMDAwMAkJMDAwMDAwMDAJCQkwMDDwMDAwMDAwMOWAgA0wMDAwMDCbSTCbOUkw (base64)

000000000000�4h0000		00000000			000�0000000倀
000000�I0�9I0

Failing assert(self.screen.cursor.x > 0) (FIXED)

maybe deleteChars related?

Traceback

thread 00000000 panic: reached unreachable code
std/debug.zig:342:14: 0x2481cc in assert (fuzz)
    if (!ok) unreachable; // assertion failure
             ^
src/terminal/Terminal.zig:890:15: 0x24f429 in print (fuzz)
        assert(self.screen.cursor.x > 0);
              ^
fuzz.zig:183:32: 0x251652 in print (fuzz)
        try self.terminal.print(ch);
                               ^
src/terminal/stream.zig:66:83: 0x271118 in next (fuzz)
                    .print => |p| if (@hasDecl(T, "print")) try self.handler.print(p),
                                                                                  ^
fuzz.zig:54:16: 0x242f48 in fuzz_next (fuzz)
    stream.next(c) catch return true;
               ^

Input

MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwCeswMDD0MDAwMDAwMDAwMDAw6JubDRtbNjlQMA== (base64)

000000000000000000000000000000000000000000000000	�000�000000000000蛛
�[69P0

cursorLeft

Overflow in self.screen.cursor.y - 1

via backspace

Traceback

thread 00000000 panic: integer overflow
src/terminal/Terminal.zig:1443:74: 0x263e1b in cursorLeft (fuzz)
        const row = self.screen.getRow(.{ .active = self.screen.cursor.y - 1 });
                                                                         ^
src/terminal/Terminal.zig:1517:20: 0x25170d in backspace (fuzz)
    self.cursorLeft(1);
                   ^
fuzz.zig:196:32: 0x2516dc in backspace (fuzz)
        self.terminal.backspace();
                               ^
src/terminal/stream.zig:113:47: 0x251d56 in execute (fuzz)
                    try self.handler.backspace()
                                              ^
src/terminal/stream.zig:67:56: 0x27118f in next (fuzz)
                    .execute => |code| try self.execute(code),
                                                       ^
fuzz.zig:54:16: 0x242f48 in fuzz_next (fuzz)
    stream.next(c) catch return true;
               ^

Input

mz80NWibM3II (base64)

�?45h�3r�

horizontalTabBack (FIXED)

Overflow in self.screen.cursor.x -= 1

via csiDispatch

Traceback

thread 00000000 panic: integer overflow
src/terminal/Terminal.zig:1550:30: 0x254155 in horizontalTabBack (fuzz)
        self.screen.cursor.x -= 1;
                             ^
fuzz.zig:210:48: 0x25421d in horizontalTabBack (fuzz)
            try self.terminal.horizontalTabBack();
                                               ^
src/terminal/stream.zig:428:96: 0x26584f in csiDispatch (fuzz)
                'Z' => if (@hasDecl(T, "horizontalTabBack")) try self.handler.horizontalTabBack(
                                                                                               ^
src/terminal/stream.zig:68:71: 0x271213 in next (fuzz)
                    .csi_dispatch => |csi_action| try self.csiDispatch(csi_action),
                                                                      ^
fuzz.zig:54:16: 0x242f48 in fuzz_next (fuzz)
    stream.next(c) catch return true;
               ^

Input

G1s/NmgbNxtbPzY5aBtbNXMbOBtbWg== (base64)

�[?6h�7�[?69h�[5s�8�[Z

insertBlanks (FIXED)

Overflow in right_limit - pivot

via csiDispatch

Traceback

thread 00000000 panic: integer overflow
src/terminal/Terminal.zig:1645:34: 0x251107 in insertBlanks (fuzz)
    const copyable = right_limit - pivot;
                                 ^
fuzz.zig:296:35: 0x2638db in insertBlanks (fuzz)
        self.terminal.insertBlanks(count);
                                  ^
src/terminal/stream.zig:932:55: 0x268095 in csiDispatch (fuzz)
                    1 => try self.handler.insertBlanks(action.params[0]),
                                                      ^
src/terminal/stream.zig:68:71: 0x271213 in next (fuzz)
                    .csi_dispatch => |csi_action| try self.csiDispatch(csi_action),
                                                                      ^
fuzz.zig:54:16: 0x242f48 in fuzz_next (fuzz)
    stream.next(c) catch return true;
               ^

Input

(base64)

MDAwMDAwMDAwMDAwLHRkm/8/Njn/aGhvMDAwMCwsLIEAAAAA8Dc2f28AM2xsmzt/M7l/f39zfwd+B2ibPwoECTEwMzl/f3N/B34HaJs/CgQJMTAzORwA3gAAcmeoPwAEHADeAAByZ6g/AAQJMDH//x8Hfgdomz8ABAkxMDM5AADeAAByZ6g/AASbPwAECTE0//8f3gB/AEAHaJs/AAQJMTAwNQAASJubACAtNP6AAJubACI1PwgIM///H94A3Nzc3Nzc3Nzc3Nzc3ADc3Nw0AAAANf/p/x/eAABomz8IM//cmwPo3Nw=
@mitchellh
Copy link
Contributor

Thank you very much for doing this! Looking forward to tackling some of these.

@mitchellh mitchellh added the contributor friendly A well-scoped, approachable issue for someone looking to contributor. label Oct 24, 2023
@mitchellh
Copy link
Contributor

I fixed the cursorLeft and horizontalTabBack crashes, with unit tests added in #724

@mitchellh
Copy link
Contributor

mitchellh commented Oct 24, 2023

I want to fix the crash first but just noting for my future self: ICH is being triggered with intermediates, and it should not. i.e. ESC [ ? 14 @ should not trigger ICH. This isn't causing a crash just something I noticed in the repro.

@mitchellh
Copy link
Contributor

Fixed the insertBlanks one too.

@mitchellh
Copy link
Contributor

@truemedian Any chance you can document how you ran the fuzz tests as well? It'd be nice to document that so we can do it again in the future.

@erf
Copy link
Collaborator

erf commented Oct 24, 2023

Did you try vttest ?

@mitchellh
Copy link
Contributor

mitchellh commented Oct 24, 2023

Did you try vttest ?

Yeah I regularly run vttest and it’s the gold standard I’m using for #632 (in addition to the raw source audit). We’re not 100% yet but we do much better than most terminals.

Downsides of vttest is that it’s hard to automate. And it tests a very very very very small surface area of sequences and mostly only in the happy path, not the edge cases

@erf
Copy link
Collaborator

erf commented Oct 24, 2023

Yeah maybe good to use to find issues then fix and add automated unit tests

@mitchellh
Copy link
Contributor

Simpler reproduction for the deleteChars issue:

cols=$(tput cols)
printf "\033[${cols}G" # move to last column
printf "\033[D" # move left
printf "\u6A4B"
printf "\r"
printf "\033[69P"
printf "0"

@mitchellh
Copy link
Contributor

Fixed the print deleteChars issue: 1457bce

@mitchellh
Copy link
Contributor

All terminal crashers are fixed and tested.

@mitchellh mitchellh added the crash Causes a crash, very high priority to fix label Oct 24, 2023
@mitchellh
Copy link
Contributor

All crashers fixed. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
contributor friendly A well-scoped, approachable issue for someone looking to contributor. crash Causes a crash, very high priority to fix
Projects
None yet
Development

No branches or pull requests

3 participants