authorgravatar for goon.pri.low@gmail.comKendall Condon <goon.pri.low@gmail.com> 2025-05-01 16:56:54-04:00
committergravatar for goon.pri.low@gmail.comKendall Condon <goon.pri.low@gmail.com> 2025-09-18 18:56:18-04:00
logb905c65661bd0d47e6ac7d537649eef8e29e44fc
treefce02c822f5d74c6d57303a751fd7951eea1c6c5
parente66b269333b5c400d76078318ac92354753bcf7b

add some new fuzz tests


3 files changed, 48 insertions(+), 14 deletions(-)

lib/std/json/scanner_test.zig+17
......@@ -517,3 +517,20 @@ test isNumberFormattedLikeAnInteger {
517517 try std.testing.expect(!isNumberFormattedLikeAnInteger("1e10"));
518518 try std.testing.expect(!isNumberFormattedLikeAnInteger("1E10"));
519519}
520
521test "fuzz" {
522 try std.testing.fuzz({}, fuzzTestOne, .{});
523}
524
525fn fuzzTestOne(_: void, input: []const u8) !void {
526 var buf: [16384]u8 = undefined;
527 var fba: std.heap.FixedBufferAllocator = .init(&buf);
528
529 var scanner = Scanner.initCompleteInput(fba.allocator(), input);
530 // Property: There are at most input.len tokens
531 var tokens: usize = 0;
532 while ((scanner.next() catch return) != .end_of_document) {
533 tokens += 1;
534 if (tokens > input.len) return error.Overflow;
535 }
536}
lib/std/zig/parser_test.zig+16
......@@ -6451,3 +6451,19 @@ fn testError(source: [:0]const u8, expected_errors: []const Error) !void {
64516451 try std.testing.expectEqual(expected, tree.errors[i].tag);
64526452 }
64536453}
6454
6455test "fuzz ast parse" {
6456 try std.testing.fuzz({}, fuzzTestOneParse, .{});
6457}
6458
6459fn fuzzTestOneParse(_: void, input: []const u8) !void {
6460 // The first byte holds if zig / zon
6461 if (input.len == 0) return;
6462 const mode: std.zig.Ast.Mode = if (input[0] & 1 == 0) .zig else .zon;
6463 const bytes = input[1..];
6464
6465 var fba: std.heap.FixedBufferAllocator = .init(&fixed_buffer_mem);
6466 const allocator = fba.allocator();
6467 const source = allocator.dupeZ(u8, bytes) catch return;
6468 _ = std.zig.Ast.parse(allocator, source, mode) catch return;
6469}
lib/std/zig/tokenizer.zig+15-14
......@@ -1721,10 +1721,14 @@ fn testTokenize(source: [:0]const u8, expected_token_tags: []const Token.Tag) !v
17211721 try std.testing.expectEqual(source.len, last_token.loc.end);
17221722}
17231723
1724fn testPropertiesUpheld(context: void, source: []const u8) anyerror!void {
1725 _ = context;
1726 const source0 = try std.testing.allocator.dupeZ(u8, source);
1727 defer std.testing.allocator.free(source0);
1724fn testPropertiesUpheld(_: void, source: []const u8) !void {
1725 var source0_buf: [512]u8 = undefined;
1726 if (source.len + 1 > source0_buf.len)
1727 return;
1728 @memcpy(source0_buf[0..source.len], source);
1729 source0_buf[source.len] = 0;
1730 const source0 = source0_buf[0..source.len :0];
1731
17281732 var tokenizer = Tokenizer.init(source0);
17291733 var tokenization_failed = false;
17301734 while (true) {
......@@ -1750,18 +1754,15 @@ fn testPropertiesUpheld(context: void, source: []const u8) anyerror!void {
17501754 }
17511755 }
17521756
1753 if (source0.len > 0) for (source0, source0[1..][0..source0.len]) |cur, next| {
1757 if (tokenization_failed) return;
1758 for (source0) |cur| {
17541759 // Property: No null byte allowed except at end.
17551760 if (cur == 0) {
1756 try std.testing.expect(tokenization_failed);
1757 }
1758 // Property: No ASCII control characters other than \n and \t are allowed.
1759 if (std.ascii.isControl(cur) and cur != '\n' and cur != '\t') {
1760 try std.testing.expect(tokenization_failed);
1761 return error.TestUnexpectedResult;
17611762 }
1762 // Property: All '\r' must be followed by '\n'.
1763 if (cur == '\r' and next != '\n') {
1764 try std.testing.expect(tokenization_failed);
1763 // Property: No ASCII control characters other than \n, \t, and \r are allowed.
1764 if (std.ascii.isControl(cur) and cur != '\n' and cur != '\t' and cur != '\r') {
1765 return error.TestUnexpectedResult;
17651766 }
1766 };
1767 }
17671768}