Repository navigation
crash: capacity changes during print with managed memory can be unsafe #11261
Copy link
Copy link
Closed
Copy link
Labels
crashCauses a crash, very high priority to fixCauses a crash, very high priority to fixvtControl sequence relatedControl sequence related
Milestone
Description
From #11249
Test case (not cleaned up a ton):
// Reproduces a crash found by fuzzing where Terminal.print keeps a stale
// pointer to the previous cell when a hyperlink write causes page growth.
test "Terminal: grapheme append after hyperlink growth" {
const alloc = testing.allocator;
var t = try init(alloc, .{ .cols = 80, .rows = 24 });
defer t.deinit(alloc);
t.modes.set(.grapheme_cluster, true);
try t.screens.active.startHyperlink("h", null);
try t.printString("HeHelloooooooo");
try t.print(0x1E);
try t.print(0x1E);
try t.print(0x1E);
try t.print(0x1E);
for (0..45) |_| try t.print(0xFFFD);
try t.print(0x0600);
try t.print(0xFFFD);
}Stack:
thread 1206566 panic: reached unreachable code
/nix/store/aywnv0mzxax3lgr1ynn2ix88haxy5na8-zig-0.15.2/lib/std/debug.zig:559:14: 0x104d21d47 in assert (ghostty-test)
if (!ok) unreachable; // assertion failure
^
/Users/mitchellh/Documents/ghostty/src/terminal/page.zig:1487:54: 0x104e4cf7f in appendGrapheme (ghostty-test)
if (build_options.slow_runtime_safety) assert(cell.codepoint() != 0);
^
/Users/mitchellh/Documents/ghostty/src/terminal/Screen.zig:2139:50: 0x104e3a623 in appendGrapheme (ghostty-test)
self.cursor.page_pin.node.data.appendGrapheme(
^
/Users/mitchellh/Documents/ghostty/src/terminal/Terminal.zig:533:51: 0x104e37263 in print (ghostty-test)
try self.screens.active.appendGrapheme(prev.cell, c);
^
/Users/mitchellh/Documents/ghostty/src/terminal/Terminal.zig:13022:16: 0x104e3e20f in test.Terminal: grapheme append after hyperlink growth (ghostty-test)
try t.print(0xFFFD);
^
/nix/store/aywnv0mzxax3lgr1ynn2ix88haxy5na8-zig-0.15.2/lib/compiler/test_runner.zig:130:29: 0x104dbd9db in mainServer (ghostty-test)
test_fn.func() catch |err| switch (err) {
^
/nix/store/aywnv0mzxax3lgr1ynn2ix88haxy5na8-zig-0.15.2/lib/compiler/test_runner.zig:64:26: 0x104dbafdb in main (ghostty-test)
return mainServer() catch @panic("internal test runner failure");
^
/nix/store/aywnv0mzxax3lgr1ynn2ix88haxy5na8-zig-0.15.2/lib/std/start.zig:618:22: 0x104dbaa07 in main (ghostty-test)
root.main();
Root cause:
The crash is a stale-pointer bug in grapheme-cluster handling.
What Is Happening (from AI, but verified its true):
- In Terminal.print, the grapheme path caches prev.cell early, then may reshape that cell as wide and emit a spacer via printCell.
- printCell applies active hyperlink state with cursorSetHyperlink.
- cursorSetHyperlink can call increaseCapacity, which reallocates/clones the page and reloads cursor pointers.
- The old prev.cell pointer is now invalid, but Terminal.print still uses it in appendGrapheme, triggering assertions in Page.appendGrapheme (cell.codepoint() != 0 in safety builds, row.grapheme consistency asserts in non-safety paths).
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
crashCauses a crash, very high priority to fixCauses a crash, very high priority to fixvtControl sequence relatedControl sequence related
Activity
mitchellh commentedon Mar 9, 2026
The AI-proposed solution was to recompute
prevat every point it's used, but that's too expensive for how hot path this function is. I propose instead trying to pursue a solution that recomputesprevonly if the underlying page capacity was changed during an operation.There are multiple ways to detect this, and we should consider what is cheapest...
mitchellh commentedon Mar 9, 2026
Here's another test case:
terminal: reload cell pointers when print grows a page (#13960)
terminal: reload cell pointers when print grows a page
fix(terminal): port upstream page-growth memory-safety fixes (PKG-03:…