feature. See also
. The project being documented here (as the example) is the Zig library itself.
test_runner.fuzz_runner
var fuzz_runner: if (builtin.fuzz) struct
File
Code
var fuzz_runner: if (builtin.fuzz) struct {
indexes: []u32,
server: *std.zig.Server,
gpa: std.mem.Allocator,
io: Io,
input_poller: Io.Future(Io.Cancelable!void),
comptime {
assert(builtin.fuzz);
}
export fn runner_test_run(i: u32) void {
@disableInstrumentation();
fuzz_runner.server.serveU32Message(.fuzz_test_change, i) catch |e| switch (e) {
error.WriteFailed => panic("failed to write to stdout: {t}", .{stdout_writer.err.?}),
};
testing.allocator_instance = .init(std.heap.page_allocator, .{
.canary = 0xc3a701ba,
.check_write_after_free = true,
});
defer if (testing.allocator_instance.deinit() != 0) std.process.exit(1);
is_fuzz_test = false;
builtin.test_functions[fuzz_runner.indexes[i]].func() catch |err| switch (err) {
error.SkipZigTest => return,
else => {
if (@errorReturnTrace()) |trace| {
std.debug.dumpErrorReturnTrace(trace);
}
std.debug.print("failed with error.{t}\n", .{err});
std.process.exit(1);
},
};
if (!is_fuzz_test) @panic("missed call to std.testing.fuzz");
if (log_err_count != 0) @panic("error logs detected");
}
export fn runner_test_name(i: u32) fuzz_abi.Slice {
@disableInstrumentation();
return .fromSlice(builtin.test_functions[fuzz_runner.indexes[i]].name);
}
export fn runner_broadcast_input(test_i: u32, bytes_slice: fuzz_abi.Slice) void {
@disableInstrumentation();
const bytes = bytes_slice.toSlice();
fuzz_runner.server.serveBroadcastFuzzInputMessage(test_i, bytes) catch |e| switch (e) {
error.WriteFailed => panic("failed to write to stdout: {t}", .{stdout_writer.err.?}),
};
}
export fn runner_start_input_poller() void {
@disableInstrumentation();
const future = fuzz_runner.io.concurrent(inputPoller, .{}) catch |e| switch (e) {
error.ConcurrencyUnavailable => @panic("failed to spawn concurrent fuzz input poller"),
};
fuzz_runner.input_poller = future;
}
export fn runner_stop_input_poller() void {
@disableInstrumentation();
assert(fuzz_runner.input_poller.cancel(fuzz_runner.io) == error.Canceled);
}
export fn runner_futex_wait(ptr: *const u32, expected: u32) bool {
@disableInstrumentation();
return fuzz_runner.io.futexWait(u32, ptr, expected) == error.Canceled;
}
export fn runner_futex_wake(ptr: *const u32, waiters: u32) void {
@disableInstrumentation();
fuzz_runner.io.futexWake(u32, ptr, waiters);
}
fn inputPoller() Io.Cancelable!void {
@disableInstrumentation();
switch (inputPollerInner()) {
error.Canceled => |e| return e,
error.ReadFailed => {
if (stdin_reader.err.? == error.Canceled) return error.Canceled;
panic("failed to read from stdin: {t}", .{stdin_reader.err.?});
},
error.EndOfStream => @panic("unexpected end of stdin"),
}
}
fn inputPollerInner() (Io.Cancelable || Io.Reader.Error) {
@disableInstrumentation();
const server = fuzz_runner.server;
var large_bytes_list: std.ArrayList(u8) = .empty;
defer large_bytes_list.deinit(fuzz_runner.gpa);
while (true) {
const hdr = try server.receiveMessage();
if (hdr.tag != .new_fuzz_input) {
panic("unexpected message: {x}\n", .{@backingInt(hdr.tag)});
}
const test_i = try server.receiveBody_u32();
const input_len = hdr.bytes_len - 4;
const bytes = if (input_len <= server.in.buffer.len)
try server.in.take(input_len)
else bytes: {
large_bytes_list.resize(fuzz_runner.gpa, @intCast(input_len)) catch @panic("OOM");
try server.in.readSliceAll(large_bytes_list.items);
break :bytes large_bytes_list.items;
};
if (fuzz_abi.fuzzer_receive_input(test_i, .fromSlice(bytes))) {
return error.Canceled;
}
}
}
} else void = undefined