commit c3e6ff7206c4e406bef8696171b40666ccd228b8 (tree)
parent cc2d992e2494f29d3d03f58a8ff318dd64815a1f
Author: Kendall Condon <goon.pri.low@gmail.com>
Date: Wed, 11 Mar 2026 21:12:58 -0400
libfuzzer: use error.SkipZigTest
Diffstat:
4 files changed, 23 insertions(+), 12 deletions(-)
diff --git 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
@@ -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
@@ -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
@@ -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;