authorgravatar for mail@isaacfreund.comIsaac Freund <mail@isaacfreund.com> 2026-07-06 15:20:25+02:00
committergravatar for mail@isaacfreund.comIsaac Freund <mail@isaacfreund.com> 2026-07-08 11:49:01+02:00
logb0738ef3e0354765b5aa1713d6313c8606c52f41
tree1ffafe183a7b4d7484d92e7e3aa51d02e227dece
parentd37d6d39f7442892d261f0d80cf72226462ad2c4
signaturelock-open Commit is signed but in an unrecognized format.

parser: warn on non-final varargs before further parsing

To prevent the fuzzer from being able to reach stack overflows, we need to give an error for non-final varargs (...) before parsing the next argument.

1 files changed, 6 insertions(+), 5 deletions(-)

lib/std/zig/Parse.zig+6-5
...@@ -3371,6 +3371,7 @@ fn expectContainerDeclAuto(p: *Parse) !Node.Index {...@@ -3371,6 +3371,7 @@ fn expectContainerDeclAuto(p: *Parse) !Node.Index {
3371/// Give a helpful error message for those transitioning from3371/// Give a helpful error message for those transitioning from
3372/// C's 'struct Foo {};' to Zig's 'const Foo = struct {};'.3372/// C's 'struct Foo {};' to Zig's 'const Foo = struct {};'.
3373fn parseCStyleContainer(p: *Parse) Error!bool {3373fn parseCStyleContainer(p: *Parse) Error!bool {
3374 if (!p.recover) return false;
3374 const main_token = p.tok_i;3375 const main_token = p.tok_i;
3375 switch (p.tokenTag(p.tok_i)) {3376 switch (p.tokenTag(p.tok_i)) {
3376 .keyword_enum, .keyword_union, .keyword_struct => {},3377 .keyword_enum, .keyword_union, .keyword_struct => {},
...@@ -3436,10 +3437,13 @@ fn parseParamDeclList(p: *Parse) !SmallSpan {...@@ -3436,10 +3437,13 @@ fn parseParamDeclList(p: *Parse) !SmallSpan {
3436 _ = try p.expectToken(.l_paren);3437 _ = try p.expectToken(.l_paren);
3437 const scratch_top = p.scratch.items.len;3438 const scratch_top = p.scratch.items.len;
3438 defer p.scratch.shrinkRetainingCapacity(scratch_top);3439 defer p.scratch.shrinkRetainingCapacity(scratch_top);
3439 var varargs: union(enum) { none, seen, nonfinal: TokenIndex } = .none;3440 var varargs: enum { none, seen, err } = .none;
3440 while (true) {3441 while (true) {
3441 if (p.eatToken(.r_paren)) |_| break;3442 if (p.eatToken(.r_paren)) |_| break;
3442 if (varargs == .seen) varargs = .{ .nonfinal = p.tok_i };3443 if (varargs == .seen) {
3444 try p.warnMsg(.{ .tag = .varargs_nonfinal, .token = p.tok_i });
3445 varargs = .err;
3446 }
3443 const opt_param = try p.expectParamDecl();3447 const opt_param = try p.expectParamDecl();
3444 if (opt_param) |param| {3448 if (opt_param) |param| {
3445 try p.scratch.append(p.gpa, param);3449 try p.scratch.append(p.gpa, param);
...@@ -3457,9 +3461,6 @@ fn parseParamDeclList(p: *Parse) !SmallSpan {...@@ -3457,9 +3461,6 @@ fn parseParamDeclList(p: *Parse) !SmallSpan {
3457 else => try p.warn(.expected_comma_after_param),3461 else => try p.warn(.expected_comma_after_param),
3458 }3462 }
3459 }3463 }
3460 if (varargs == .nonfinal) {
3461 try p.warnMsg(.{ .tag = .varargs_nonfinal, .token = varargs.nonfinal });
3462 }
3463 const params = p.scratch.items[scratch_top..];3464 const params = p.scratch.items[scratch_top..];
3464 return switch (params.len) {3465 return switch (params.len) {
3465 0 => .{ .zero_or_one = .none },3466 0 => .{ .zero_or_one = .none },