authorgravatar for mail@isaacfreund.comIsaac Freund <mail@isaacfreund.com> 2026-07-04 13:05:28+02:00
committergravatar for mail@isaacfreund.comIsaac Freund <mail@isaacfreund.com> 2026-07-08 11:49:01+02:00
log203e8279539220db84823bb79671c5c6c7d1065f
tree371be758619ecfb8fc1bc733d199d6fbfdfcd43b
parente5230c5e8aa95c03e62554ba31bd2f2c7ff86ed9
signaturelock-open Commit is signed but in an unrecognized format.

parser: check binary op whitespace before parsing rhs


3 files changed, 23 insertions(+), 4 deletions(-)

lib/std/zig/Parse.zig+13-4
......@@ -1637,13 +1637,17 @@ fn parseExprPrecedence(p: *Parse, min_prec: i32) Error!?Node.Index {
16371637 if (tok_tag == .keyword_catch) {
16381638 _ = try p.parsePayload();
16391639 }
1640 const rhs = try p.parseExprPrecedence(info.prec + 1) orelse {
1641 try p.warn(.expected_expr);
1642 return node;
1643 };
16441640
1641 // Check for whitespace error before parsing the rhs to facilitate fuzzing.
1642 // Consider the case where there is a mismatched whitespace error but the rhs
1643 // expression triggers stack overflow when parsing is attempted. In this case
1644 // we want to return with an error rather than crashing on stack overflow.
16451645 {
16461646 const tok_len = tok_tag.lexeme().?.len;
1647 if (p.tokenStart(oper_token) + tok_len >= p.source.len) {
1648 try p.warn(.expected_expr);
1649 return node;
1650 }
16471651 const char_before = p.source[p.tokenStart(oper_token) - 1];
16481652 const char_after = p.source[p.tokenStart(oper_token) + tok_len];
16491653 if (tok_tag == .ampersand and char_after == '&') {
......@@ -1655,6 +1659,11 @@ fn parseExprPrecedence(p: *Parse, min_prec: i32) Error!?Node.Index {
16551659 }
16561660 }
16571661
1662 const rhs = try p.parseExprPrecedence(info.prec + 1) orelse {
1663 try p.warn(.expected_expr);
1664 return node;
1665 };
1666
16581667 node = try p.addNode(.{
16591668 .tag = info.tag,
16601669 .main_token = oper_token,
lib/std/zig/parser_fuzz.zig+9
......@@ -131,6 +131,15 @@ test "nosuspend multi assign" {
131131 try checkAgainstOracle("test{nosuspend*0,var _=0;}");
132132}
133133
134// Found using AFL++
135test "bin op at end of file" {
136 try checkAgainstOracle(
137 \\test {
138 \\ _ = 00
139 \\|
140 );
141}
142
134143fn checkAgainstOracle(source: [:0]const u8) !void {
135144 var fba_buf: [1 << 18]u8 = undefined;
136145 var fba: std.heap.FixedBufferAllocator = .init(&fba_buf);
lib/std/zig/parser_test.zig+1
......@@ -6976,6 +6976,7 @@ test "recovery: missing comma" {
69766976 , &[_]Error{
69776977 .expected_comma_after_switch_prong,
69786978 .expected_comma_after_switch_prong,
6979 .mismatched_binary_op_whitespace,
69796980 .expected_expr,
69806981 });
69816982}