authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-09-25 17:16:10-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-09-25 17:16:10-07:00
log2da8ec9865b6b086341b0f01334f27a488bc220a
tree4e3678edcca1a80eabea7bfa369188e4016e7a5b
parent98253bc0eee2bace0ec1689126a8dd853d296877

fuzzing: fix off-by-one in limit count


1 files changed, 44 insertions(+), 44 deletions(-)

lib/fuzzer.zig+44-44
...@@ -512,7 +512,7 @@ const Fuzzer = struct {...@@ -512,7 +512,7 @@ const Fuzzer = struct {
512 self.corpus_pos = 0;512 self.corpus_pos = 0;
513513
514 const rng = self.rng.random();514 const rng = self.rng.random();
515 while (true) {515 const m = while (true) {
516 const m = self.mutations.items[rng.uintLessThanBiased(usize, self.mutations.items.len)];516 const m = self.mutations.items[rng.uintLessThanBiased(usize, self.mutations.items.len)];
517 if (!m.mutate(517 if (!m.mutate(
518 rng,518 rng,
...@@ -524,53 +524,53 @@ const Fuzzer = struct {...@@ -524,53 +524,53 @@ const Fuzzer = struct {
524 inst.const_vals8.items,524 inst.const_vals8.items,
525 inst.const_vals16.items,525 inst.const_vals16.items,
526 )) continue;526 )) continue;
527 break m;
528 };
527529
528 self.run();530 self.run();
529 if (inst.isFresh()) {
530 @branchHint(.unlikely);
531
532 const header = mem.bytesAsValue(
533 abi.SeenPcsHeader,
534 exec.shared_seen_pcs.items[0..@sizeOf(abi.SeenPcsHeader)],
535 );
536 _ = @atomicRmw(usize, &header.unique_runs, .Add, 1, .monotonic);
537531
538 inst.setFresh();532 if (inst.isFresh()) {
539 self.minimizeInput();533 @branchHint(.unlikely);
540 inst.updateSeen();
541
542 // An empty-input has always been tried, so if an empty input is fresh then the
543 // test has to be non-deterministic. This has to be checked as duplicate empty
544 // entries are not allowed.
545 if (self.input.items.len - 8 == 0) {
546 std.log.warn("non-deterministic test (empty input produces different hits)", .{});
547 _ = @atomicRmw(usize, &header.unique_runs, .Sub, 1, .monotonic);
548 return;
549 }
550534
551 const arena = self.arena_ctx.allocator();535 const header = mem.bytesAsValue(
552 const bytes = arena.dupe(u8, @volatileCast(self.input.items[8..])) catch @panic("OOM");536 abi.SeenPcsHeader,
553537 exec.shared_seen_pcs.items[0..@sizeOf(abi.SeenPcsHeader)],
554 self.corpus.append(gpa, bytes) catch @panic("OOM");538 );
555 self.mutations.appendNTimes(gpa, m, 6) catch @panic("OOM");539 _ = @atomicRmw(usize, &header.unique_runs, .Add, 1, .monotonic);
556540
557 // Write new corpus to cache541 inst.setFresh();
558 var name_buf: [@sizeOf(usize) * 2]u8 = undefined;542 self.minimizeInput();
559 self.corpus_dir.writeFile(.{543 inst.updateSeen();
560 .sub_path = std.fmt.bufPrint(544
561 &name_buf,545 // An empty-input has always been tried, so if an empty input is fresh then the
562 "{x}",546 // test has to be non-deterministic. This has to be checked as duplicate empty
563 .{self.corpus_dir_idx},547 // entries are not allowed.
564 ) catch unreachable,548 if (self.input.items.len - 8 == 0) {
565 .data = bytes,549 std.log.warn("non-deterministic test (empty input produces different hits)", .{});
566 }) catch |e| panic(550 _ = @atomicRmw(usize, &header.unique_runs, .Sub, 1, .monotonic);
567 "failed to write corpus file '{x}': {t}",551 return;
568 .{ self.corpus_dir_idx, e },
569 );
570 self.corpus_dir_idx += 1;
571 }552 }
572553
573 break;554 const arena = self.arena_ctx.allocator();
555 const bytes = arena.dupe(u8, @volatileCast(self.input.items[8..])) catch @panic("OOM");
556
557 self.corpus.append(gpa, bytes) catch @panic("OOM");
558 self.mutations.appendNTimes(gpa, m, 6) catch @panic("OOM");
559
560 // Write new corpus to cache
561 var name_buf: [@sizeOf(usize) * 2]u8 = undefined;
562 self.corpus_dir.writeFile(.{
563 .sub_path = std.fmt.bufPrint(
564 &name_buf,
565 "{x}",
566 .{self.corpus_dir_idx},
567 ) catch unreachable,
568 .data = bytes,
569 }) catch |e| panic(
570 "failed to write corpus file '{x}': {t}",
571 .{ self.corpus_dir_idx, e },
572 );
573 self.corpus_dir_idx += 1;
574 }574 }
575 }575 }
576};576};
...@@ -618,7 +618,7 @@ export fn fuzzer_new_input(bytes: abi.Slice) void {...@@ -618,7 +618,7 @@ export fn fuzzer_new_input(bytes: abi.Slice) void {
618export fn fuzzer_main(limit_kind: abi.LimitKind, amount: u64) void {618export fn fuzzer_main(limit_kind: abi.LimitKind, amount: u64) void {
619 switch (limit_kind) {619 switch (limit_kind) {
620 .forever => while (true) fuzzer.cycle(),620 .forever => while (true) fuzzer.cycle(),
621 .iterations => for (0..amount -| 1) |_| fuzzer.cycle(),621 .iterations => for (0..amount) |_| fuzzer.cycle(),
622 }622 }
623}623}
624624