| ... | @@ -512,7 +512,7 @@ const Fuzzer = struct { | ... | @@ -512,7 +512,7 @@ const Fuzzer = struct { |
| 512 | self.corpus_pos = 0; | 512 | self.corpus_pos = 0; |
| 513 | | 513 | |
| 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 | }; |
| 527 | | 529 | |
| 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); | | |
| 537 | | 531 | |
| 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 | } | | |
| 550 | | 534 | |
| 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, |
| 553 | | 537 | 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); |
| 556 | | 540 | |
| 557 | // Write new corpus to cache | 541 | 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 | } |
| 572 | | 553 | |
| 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 { |
| 618 | export fn fuzzer_main(limit_kind: abi.LimitKind, amount: u64) void { | 618 | export 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 | } |
| 624 | | 624 | |