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.
This commit is contained in:
Mitchell Hashimoto
2026-02-28 21:03:49 -08:00
parent 149949190e
commit 12f43dfb7d

View File

@@ -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);
}