From 12f43dfb7df9d04352616e29bfe0b2be9120bd96 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 28 Feb 2026 21:03:49 -0800 Subject: [PATCH] fix(terminal): bounds check params in DCS passthrough entry When a DCS sequence has more than MAX_PARAMS parameters, entering dcs_passthrough would write to params[params_idx] without a bounds check, causing an out-of-bounds access. Drop the entire DCS hook when params overflow, consistent with how csi_dispatch handles it. Found by AFL fuzzing. --- src/terminal/Parser.zig | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/terminal/Parser.zig b/src/terminal/Parser.zig index 34a23787f..88e7edf7c 100644 --- a/src/terminal/Parser.zig +++ b/src/terminal/Parser.zig @@ -289,6 +289,8 @@ pub fn next(self: *Parser, c: u8) [3]?Action { break :osc_string null; }, .dcs_passthrough => dcs_hook: { + // Ignore too many parameters + if (self.params_idx >= MAX_PARAMS) break :dcs_hook null; // Finalize parameters if (self.param_acc_idx > 0) { self.params[self.params_idx] = self.param_acc; @@ -1070,3 +1072,28 @@ test "dcs: params" { try testing.expectEqual('p', hook.final); } } + +test "dcs: too many params" { + // Regression test for a crash found by fuzzing (afl). When a DCS + // sequence has more than MAX_PARAMS parameters and param_acc_idx > 0, + // entering dcs_passthrough wrote to params[params_idx] without a + // bounds check, causing an out-of-bounds access. + var p = init(); + _ = p.next(0x1B); // ESC + _ = p.next('P'); // DCS entry + + // Feed a digit then MAX_PARAMS semicolons to fill all param slots. + _ = p.next('6'); + for (0..MAX_PARAMS) |_| { + _ = p.next(';'); + } + // Feed another digit so param_acc_idx > 0 while params_idx == MAX_PARAMS. + _ = p.next('7'); + + // A final byte triggers entry to dcs_passthrough. The DCS should + // be dropped entirely, consistent with how CSI handles overflow. + const a = p.next('p'); + try testing.expect(a[0] == null); + try testing.expect(a[1] == null); + try testing.expect(a[2] == null); +}