Saving fuzzer crash files as reproductions

When the fuzzer finds a crash it saves a file to .zig-cache/f/crash and seems to continue fuzzing. Is that true? If so, are crash files overwritten on new crashes?

I’d like to automate saving the crash files on each crash so they can be run as tests. Has anyone else has done similar?

EDIT

I’ve been doing the following and it gives me reproductions.

fn fuzzFile(io: std.Io, path: []const u8) !void {
    const contents = std.Io.Dir.cwd().readFileAlloc(io, path, testgpa, .unlimited) catch return;
    defer testgpa.free(contents);
    var smith = testing.Smith{ .in = contents };
    try fuzz1(&smith, testgpa);
}

test "fuzz crash" {
    try fuzzOracleFile(testing.io, ".zig-cache/f/crash");
}

@gooncreeper any thoughts on how to automate saving the crash files or any of this?

I recommend symlinking (or copying every time) the crash file into the source directory and doing something akin to the following while testing:

test "fuzz test" {
    try std.testing.fuzz(fuzzCtx, fuzzFn, .{
        .corpus = &.{ @embedFile("crash") },
    });
}

I also usually take advantage of @import("builtin").fuzz to print a human-readable input when rerunning in debug mode. Note that you will see two runs, one with your actual case and one with an empty input.

To answer your first question, the fuzzer does not continue running once it finds a crash, though the build system continues running for the webui.

2 Likes

Thanks! I didn’t think to provide a corpus. I’ll have to build up the corpus contents from files in my testdata/crashes folder.

Will also look into `@import(“builtin”).fuzz and human readable input.

Good to know this. Makes sense now whats happening. I guess we’re limited to one crash per fuzzing run. Hopefully we can continue fuzzing with some kind of strategy for handling multiple crash files in the future.

Thanks for your work on the fuzzer! I’m getting a lot of mileage out of it as-is running for a bit and copying files manually.

3 Likes