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