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(...@@ -406,13 +406,13 @@ pub fn fuzz(
406 const global = struct {406 const global = struct {
407 var ctx: @TypeOf(context) = undefined;407 var ctx: @TypeOf(context) = undefined;
408408
409 fn test_one() callconv(.c) void {409 fn test_one() callconv(.c) bool {
410 @disableInstrumentation();410 @disableInstrumentation();
411 testing.allocator_instance = .{};411 testing.allocator_instance = .{};
412 defer if (testing.allocator_instance.deinit() == .leak) std.process.exit(1);412 defer if (testing.allocator_instance.deinit() == .leak) std.process.exit(1);
413 log_err_count = 0;413 log_err_count = 0;
414 testOne(ctx, @constCast(&testing.Smith{ .in = null })) catch |err| switch (err) {414 testOne(ctx, @constCast(&testing.Smith{ .in = null })) catch |err| switch (err) {
415 error.SkipZigTest => return,415 error.SkipZigTest => return true,
416 else => {416 else => {
417 const stderr = std.debug.lockStderr(&.{}).terminal();417 const stderr = std.debug.lockStderr(&.{}).terminal();
418 p: {418 p: {
...@@ -429,6 +429,7 @@ pub fn fuzz(...@@ -429,6 +429,7 @@ pub fn fuzz(
429 stderr.writer.print("error logs detected\n", .{}) catch {};429 stderr.writer.print("error logs detected\n", .{}) catch {};
430 std.process.exit(1);430 std.process.exit(1);
431 }431 }
432 return false;
432 }433 }
433 };434 };
434 if (builtin.fuzz) {435 if (builtin.fuzz) {
lib/fuzzer.zig+15-8
...@@ -686,7 +686,7 @@ const Fuzzer = struct {...@@ -686,7 +686,7 @@ const Fuzzer = struct {
686 const len = mem.readInt(u32, f.mmap_input.mmap.memory[0..4], .little);686 const len = mem.readInt(u32, f.mmap_input.mmap.memory[0..4], .little);
687 if (len < f.mmap_input.mmap.memory[4..].len) {687 if (len < f.mmap_input.mmap.memory[4..].len) {
688 f.mmap_input.len = len;688 f.mmap_input.len = len;
689 f.runBytes(f.mmap_input.inputSlice(), .bytes_dry);689 _ = f.runBytes(f.mmap_input.inputSlice(), .bytes_dry);
690 f.mmap_input.clearRetainingCapacity();690 f.mmap_input.clearRetainingCapacity();
691 }691 }
692 }692 }
...@@ -761,12 +761,13 @@ const Fuzzer = struct {...@@ -761,12 +761,13 @@ const Fuzzer = struct {
761 return fresh;761 return fresh;
762 }762 }
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 {
765 assert(mode == .bytes_dry or mode == .bytes_fresh);766 assert(mode == .bytes_dry or mode == .bytes_fresh);
766767
767 f.bytes_input = .{ .in = bytes };768 f.bytes_input = .{ .in = bytes };
768 f.corpus_pos = mode;769 f.corpus_pos = mode;
769 f.run(0); // 0 since `f.uid_data` is unused770 return f.run(0); // 0 since `f.uid_data` is unused
770 }771 }
771772
772 fn updateSeenPcs(f: *Fuzzer) void {773 fn updateSeenPcs(f: *Fuzzer) void {
...@@ -871,7 +872,11 @@ const Fuzzer = struct {...@@ -871,7 +872,11 @@ const Fuzzer = struct {
871872
872 fn newInput(f: *Fuzzer, modify_fs_corpus: bool) void {873 fn newInput(f: *Fuzzer, modify_fs_corpus: bool) void {
873 const bytes = f.mmap_input.inputSlice();874 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;
875 f.req_values = f.input_builder.total_ints + f.input_builder.total_bytes;880 f.req_values = f.input_builder.total_ints + f.input_builder.total_bytes;
876 f.req_bytes = @intCast(f.input_builder.bytes_table.items.len);881 f.req_bytes = @intCast(f.input_builder.bytes_table.items.len);
877 var input = f.input_builder.build();882 var input = f.input_builder.build();
...@@ -1005,15 +1010,17 @@ const Fuzzer = struct {...@@ -1005,15 +1010,17 @@ const Fuzzer = struct {
1005 panic("failed to write corpus file '{s}': {t}", .{ name, e });1010 panic("failed to write corpus file '{s}': {t}", .{ name, e });
1006 }1011 }
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 {
1009 @memset(exec.pc_counters, 0);1015 @memset(exec.pc_counters, 0);
1010 f.uid_data_i.items.len = input_uids;1016 f.uid_data_i.items.len = input_uids;
1011 @memset(f.uid_data_i.items, 0);1017 @memset(f.uid_data_i.items, 0);
1012 f.req_values = 0;1018 f.req_values = 0;
1013 f.req_bytes = 0;1019 f.req_bytes = 0;
10141020
1015 f.test_one();1021 const skip = f.test_one();
1016 _ = @atomicRmw(usize, &exec.seenPcsHeader().n_runs, .Add, 1, .monotonic);1022 _ = @atomicRmw(usize, &exec.seenPcsHeader().n_runs, .Add, 1, .monotonic);
1023 return skip;
1017 }1024 }
10181025
1019 /// Returns a number of mutations to perform from 1-41026 /// Returns a number of mutations to perform from 1-4
...@@ -1085,8 +1092,8 @@ const Fuzzer = struct {...@@ -1085,8 +1092,8 @@ const Fuzzer = struct {
1085 i.* = data.order[order_i];1092 i.* = data.order[order_i];
1086 };1093 };
10871094
1088 f.run(data.uid_slices.entries.len);1095 const skip = f.run(data.uid_slices.entries.len);
1089 if (f.isFresh()) {1096 if (!skip and f.isFresh()) {
1090 @branchHint(.unlikely);1097 @branchHint(.unlikely);
10911098
1092 _ = @atomicRmw(usize, &exec.seenPcsHeader().unique_runs, .Add, 1, .monotonic);1099 _ = @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 {...@@ -139,7 +139,8 @@ pub const Rebuild = extern struct {
139139
140/// ABI bits specifically relating to the fuzzer interface.140/// ABI bits specifically relating to the fuzzer interface.
141pub const fuzz = struct {141pub 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
144 /// A unique value to identify the related requests across runs145 /// A unique value to identify the related requests across runs
145 pub const Uid = packed struct(u32) {146 pub const Uid = packed struct(u32) {
test/standalone/libfuzzer/main.zig+3-1
...@@ -2,7 +2,9 @@ const std = @import("std");...@@ -2,7 +2,9 @@ const std = @import("std");
2const abi = std.Build.abi.fuzz;2const abi = std.Build.abi.fuzz;
3const native_endian = @import("builtin").cpu.arch.endian();3const native_endian = @import("builtin").cpu.arch.endian();
44
5fn testOne() callconv(.c) void {}5fn testOne() callconv(.c) bool {
6 return false;
7}
68
7pub fn main(init: std.process.Init) !void {9pub fn main(init: std.process.Init) !void {
8 const gpa = init.gpa;10 const gpa = init.gpa;