diff --git a/src/terminal/sgr.zig b/src/terminal/sgr.zig index 6fd4f1e79..314d60686 100644 --- a/src/terminal/sgr.zig +++ b/src/terminal/sgr.zig @@ -249,7 +249,16 @@ pub const Parser = struct { // Colons are fairly rare in the wild. @branchHint(.unlikely); - assert(slice.len >= 2); + // A trailing colon with no following sub-param + // (e.g. "ESC[58:4:m") leaves the colon separator + // bit set on the last param without adding another + // entry, so we can see param 4 with a colon but + // nothing after it. + if (slice.len < 2) { + @branchHint(.cold); + break :underline; + } + if (self.isColon()) { // Invalid/unknown SGRs are just not very likely. @branchHint(.cold); @@ -1068,3 +1077,30 @@ test "sgr: kakoune input issue underline, fg, and bg" { try testing.expect(p.next() == null); } + +// Fuzz crash: afl-out/stream/default/crashes/id:000021 +// Input "ESC [ 5 8 : 4 : m" produces params [58, 4] with colon +// separator bits set at indices 0 and 1. The trailing colon causes +// the second iteration to see param 4 (underline) with a colon, +// triggering assert(slice.len >= 2) with slice.len == 1. +test "sgr: underline colon with trailing separator and short slice" { + var p: Parser = .{ + .params = &[_]u16{ 58, 4 }, + .params_sep = sep: { + var list = SepList.initEmpty(); + list.set(0); + list.set(1); + break :sep list; + }, + }; + + // 58:4 is not a valid underline color (sub-param 4 is not 2 or 5), + // so it falls through as unknown. + try testing.expect(p.next().? == .unknown); + + // Param 4 with a trailing colon but no sub-param is malformed, + // so it also falls through as unknown rather than panicking. + try testing.expect(p.next().? == .unknown); + + try testing.expect(p.next() == null); +}