authorgravatar for goon.pri.low@gmail.comKendall Condon <goon.pri.low@gmail.com> 2026-03-11 21:12:58-04:00
committergravatar for goon.pri.low@gmail.comKendall Condon <goon.pri.low@gmail.com> 2026-03-11 21:14:46-04:00
logc3e6ff7206c4e406bef8696171b40666ccd228b8
tree8de250ded7db745cb0c5ab799ea29bb93667b3d6
parentcc2d992e2494f29d3d03f58a8ff318dd64815a1f

libfuzzer: use error.SkipZigTest


4 files changed, 23 insertions(+), 12 deletions(-)

lib/compiler/test_runner.zig+3-2
......@@ -406,13 +406,13 @@ pub fn fuzz(
406406 const global = struct {
407407 var ctx: @TypeOf(context) = undefined;
408408
409 fn test_one() callconv(.c) void {
409 fn test_one() callconv(.c) bool {
410410 @disableInstrumentation();
411411 testing.allocator_instance = .{};
412412 defer if (testing.allocator_instance.deinit() == .leak) std.process.exit(1);
413413 log_err_count = 0;
414414 testOne(ctx, @constCast(&testing.Smith{ .in = null })) catch |err| switch (err) {
415 error.SkipZigTest => return,
415 error.SkipZigTest => return true,
416416 else => {
417417 const stderr = std.debug.lockStderr(&.{}).terminal();
418418 p: {
......@@ -429,6 +429,7 @@ pub fn fuzz(
429429 stderr.writer.print("error logs detected\n", .{}) catch {};
430430 std.process.exit(1);
431431 }
432 return false;
432433 }
433434 };
434435 if (builtin.fuzz) {
lib/fuzzer.zig+15-8
......@@ -686,7 +686,7 @@ const Fuzzer = struct {
686686 const len = mem.readInt(u32, f.mmap_input.mmap.memory[0..4], .little);
687687 if (len < f.mmap_input.mmap.memory[4..].len) {
688688 f.mmap_input.len = len;
689 f.runBytes(f.mmap_input.inputSlice(), .bytes_dry);
689 _ = f.runBytes(f.mmap_input.inputSlice(), .bytes_dry);
690690 f.mmap_input.clearRetainingCapacity();
691691 }
692692 }
......@@ -761,12 +761,13 @@ const Fuzzer = struct {
761761 return fresh;
762762 }
763763
764 fn runBytes(f: *Fuzzer, bytes: []const u8, mode: Input.Index) void {
764 /// Returns if `error.SkipZigTest` was indicated
765 fn runBytes(f: *Fuzzer, bytes: []const u8, mode: Input.Index) bool {
765766 assert(mode == .bytes_dry or mode == .bytes_fresh);
766767
767768 f.bytes_input = .{ .in = bytes };
768769 f.corpus_pos = mode;
769 f.run(0); // 0 since `f.uid_data` is unused
770 return f.run(0); // 0 since `f.uid_data` is unused
770771 }
771772
772773 fn updateSeenPcs(f: *Fuzzer) void {
......@@ -871,7 +872,11 @@ const Fuzzer = struct {
871872
872873 fn newInput(f: *Fuzzer, modify_fs_corpus: bool) void {
873874 const bytes = f.mmap_input.inputSlice();
874 f.runBytes(bytes, .bytes_fresh);
875 // `error.SkipZigTest` here can be from one of these causes:
876 // * The test has changed and a previous corpus input is being used
877 // * An input provided by the test results in it
878 // * The test is non-deterministic
879 if (f.runBytes(bytes, .bytes_fresh)) return;
875880 f.req_values = f.input_builder.total_ints + f.input_builder.total_bytes;
876881 f.req_bytes = @intCast(f.input_builder.bytes_table.items.len);
877882 var input = f.input_builder.build();
......@@ -1005,15 +1010,17 @@ const Fuzzer = struct {
10051010 panic("failed to write corpus file '{s}': {t}", .{ name, e });
10061011 }
10071012
1008 fn run(f: *Fuzzer, input_uids: usize) void {
1013 /// Returns if `error.SkipZigTest` was indicated
1014 fn run(f: *Fuzzer, input_uids: usize) bool {
10091015 @memset(exec.pc_counters, 0);
10101016 f.uid_data_i.items.len = input_uids;
10111017 @memset(f.uid_data_i.items, 0);
10121018 f.req_values = 0;
10131019 f.req_bytes = 0;
10141020
1015 f.test_one();
1021 const skip = f.test_one();
10161022 _ = @atomicRmw(usize, &exec.seenPcsHeader().n_runs, .Add, 1, .monotonic);
1023 return skip;
10171024 }
10181025
10191026 /// Returns a number of mutations to perform from 1-4
......@@ -1085,8 +1092,8 @@ const Fuzzer = struct {
10851092 i.* = data.order[order_i];
10861093 };
10871094
1088 f.run(data.uid_slices.entries.len);
1089 if (f.isFresh()) {
1095 const skip = f.run(data.uid_slices.entries.len);
1096 if (!skip and f.isFresh()) {
10901097 @branchHint(.unlikely);
10911098
10921099 _ = @atomicRmw(usize, &exec.seenPcsHeader().unique_runs, .Add, 1, .monotonic);
lib/std/Build/abi.zig+2-1
......@@ -139,7 +139,8 @@ pub const Rebuild = extern struct {
139139
140140/// ABI bits specifically relating to the fuzzer interface.
141141pub const fuzz = struct {
142 pub const TestOne = *const fn () callconv(.c) void;
142 /// Returns if `error.SkipZigTest` was indicated
143 pub const TestOne = *const fn () callconv(.c) bool;
143144
144145 /// A unique value to identify the related requests across runs
145146 pub const Uid = packed struct(u32) {
test/standalone/libfuzzer/main.zig+3-1
......@@ -2,7 +2,9 @@ const std = @import("std");
22const abi = std.Build.abi.fuzz;
33const native_endian = @import("builtin").cpu.arch.endian();
44
5fn testOne() callconv(.c) void {}
5fn testOne() callconv(.c) bool {
6 return false;
7}
68
79pub fn main(init: std.process.Init) !void {
810 const gpa = init.gpa;