diff --git a/lib/compiler/test_runner.zig b/lib/compiler/test_runner.zig index db8958519a..b2e9c797ef 100644 --- a/lib/compiler/test_runner.zig +++ b/lib/compiler/test_runner.zig @@ -406,13 +406,13 @@ pub fn fuzz( const global = struct { var ctx: @TypeOf(context) = undefined; - fn test_one() callconv(.c) void { + fn test_one() callconv(.c) bool { @disableInstrumentation(); testing.allocator_instance = .{}; defer if (testing.allocator_instance.deinit() == .leak) std.process.exit(1); log_err_count = 0; testOne(ctx, @constCast(&testing.Smith{ .in = null })) catch |err| switch (err) { - error.SkipZigTest => return, + error.SkipZigTest => return true, else => { const stderr = std.debug.lockStderr(&.{}).terminal(); p: { @@ -429,6 +429,7 @@ pub fn fuzz( stderr.writer.print("error logs detected\n", .{}) catch {}; std.process.exit(1); } + return false; } }; if (builtin.fuzz) { diff --git a/lib/fuzzer.zig b/lib/fuzzer.zig index 501cdfae7c..742bf39125 100644 --- a/lib/fuzzer.zig +++ b/lib/fuzzer.zig @@ -686,7 +686,7 @@ const Fuzzer = struct { const len = mem.readInt(u32, f.mmap_input.mmap.memory[0..4], .little); if (len < f.mmap_input.mmap.memory[4..].len) { f.mmap_input.len = len; - f.runBytes(f.mmap_input.inputSlice(), .bytes_dry); + _ = f.runBytes(f.mmap_input.inputSlice(), .bytes_dry); f.mmap_input.clearRetainingCapacity(); } } @@ -761,12 +761,13 @@ const Fuzzer = struct { return fresh; } - fn runBytes(f: *Fuzzer, bytes: []const u8, mode: Input.Index) void { + /// Returns if `error.SkipZigTest` was indicated + fn runBytes(f: *Fuzzer, bytes: []const u8, mode: Input.Index) bool { assert(mode == .bytes_dry or mode == .bytes_fresh); f.bytes_input = .{ .in = bytes }; f.corpus_pos = mode; - f.run(0); // 0 since `f.uid_data` is unused + return f.run(0); // 0 since `f.uid_data` is unused } fn updateSeenPcs(f: *Fuzzer) void { @@ -871,7 +872,11 @@ const Fuzzer = struct { fn newInput(f: *Fuzzer, modify_fs_corpus: bool) void { const bytes = f.mmap_input.inputSlice(); - f.runBytes(bytes, .bytes_fresh); + // `error.SkipZigTest` here can be from one of these causes: + // * The test has changed and a previous corpus input is being used + // * An input provided by the test results in it + // * The test is non-deterministic + if (f.runBytes(bytes, .bytes_fresh)) return; f.req_values = f.input_builder.total_ints + f.input_builder.total_bytes; f.req_bytes = @intCast(f.input_builder.bytes_table.items.len); var input = f.input_builder.build(); @@ -1005,15 +1010,17 @@ const Fuzzer = struct { panic("failed to write corpus file '{s}': {t}", .{ name, e }); } - fn run(f: *Fuzzer, input_uids: usize) void { + /// Returns if `error.SkipZigTest` was indicated + fn run(f: *Fuzzer, input_uids: usize) bool { @memset(exec.pc_counters, 0); f.uid_data_i.items.len = input_uids; @memset(f.uid_data_i.items, 0); f.req_values = 0; f.req_bytes = 0; - f.test_one(); + const skip = f.test_one(); _ = @atomicRmw(usize, &exec.seenPcsHeader().n_runs, .Add, 1, .monotonic); + return skip; } /// Returns a number of mutations to perform from 1-4 @@ -1085,8 +1092,8 @@ const Fuzzer = struct { i.* = data.order[order_i]; }; - f.run(data.uid_slices.entries.len); - if (f.isFresh()) { + const skip = f.run(data.uid_slices.entries.len); + if (!skip and f.isFresh()) { @branchHint(.unlikely); _ = @atomicRmw(usize, &exec.seenPcsHeader().unique_runs, .Add, 1, .monotonic); diff --git a/lib/std/Build/abi.zig b/lib/std/Build/abi.zig index a7decb09da..0baad7f19f 100644 --- a/lib/std/Build/abi.zig +++ b/lib/std/Build/abi.zig @@ -139,7 +139,8 @@ pub const Rebuild = extern struct { /// ABI bits specifically relating to the fuzzer interface. pub const fuzz = struct { - pub const TestOne = *const fn () callconv(.c) void; + /// Returns if `error.SkipZigTest` was indicated + pub const TestOne = *const fn () callconv(.c) bool; /// A unique value to identify the related requests across runs pub const Uid = packed struct(u32) { diff --git a/test/standalone/libfuzzer/main.zig b/test/standalone/libfuzzer/main.zig index b92ff9372d..e54117e86b 100644 --- a/test/standalone/libfuzzer/main.zig +++ b/test/standalone/libfuzzer/main.zig @@ -2,7 +2,9 @@ const std = @import("std"); const abi = std.Build.abi.fuzz; const native_endian = @import("builtin").cpu.arch.endian(); -fn testOne() callconv(.c) void {} +fn testOne() callconv(.c) bool { + return false; +} pub fn main(init: std.process.Init) !void { const gpa = init.gpa;