Skip to content

write fuzz inputs to a shared memory region before running a task #20803

@andrewrk

Description

@andrewrk

Extracted from #20773.

Currently, a fuzz test failure looks like this:

andy@bark ~/t/abc> zig build test --fuzz 
test
└─ run test failure
/home/andy/local/lib/zig/std/testing.zig:546:14: 0x11575c9 in expect (test)
    if (!ok) return error.TestUnexpectedResult;
             ^
/home/andy/tmp/abc/src/main.zig:28:5: 0x1157691 in test.fuzz example (test)
    try std.testing.expect(!std.mem.eql(u8, "canyoufindme", input_bytes));
    ^
failed with error.TestUnexpectedResult
error: the following command exited with error code 1:
/home/andy/tmp/abc/.zig-cache/o/eea1979fed4d51bc1ca0d161af979e22/test --seed=0x48fe2aeb --listen=- 
error: all fuzz workers crashed
error: the following build command failed with exit code 1:
/home/andy/tmp/abc/.zig-cache/o/bc5565bbec3a56db01acb2ab6b348742/build /home/andy/local/bin/zig /home/andy/local/lib/zig /home/andy/tmp/abc /home/andy/tmp/abc/.zig-cache /home/andy/.cache/zig --seed 0x48fe2aeb -Zb2ede88d1c7627c9 test --fuzz

If you rerun that command that it printed, it does not in fact reproduce the issue:

andy@bark ~/t/abc [1]> /home/andy/tmp/abc/.zig-cache/o/eea1979fed4d51bc1ca0d161af979e22/test --seed=0x48fe2aeb
All 2 tests passed.
1 fuzz tests found.

This is due to lack of communication between parent process (build runner) and fuzzing process (test runner).

However, for performance purposes, we don't want any communication between those processes in the hot path. That means we cannot send a message containing the current input before trying it.

Options are:

Follow the lead from other fuzzers by having a "corpus" directory, which is a list of files memory mapped into the fuzzer process, one per "interesting" input, with filenames corresponding to the run IDs. Advantages to this approach is that it's easy to recover and it could be used to share state across processes. Disadvantage is that it writes to the filesystem in a hot path. Maybe that's OK in practice? I'll have to check.

Another idea that I had is to have the parent process (build runner) create and share a memory mapping with the fuzzing process (test runner). The fuzzer would use this memory to store its most recent input(s) as well as some metadata (for example stats to display on the UI). The parent process can then read from this shared mapping to display the stats in real time as well as to recover inputs when the fuzzer process crashes.

It might not be such a bad idea to send a message when an "interesting" input is found. This message would perhaps be forwarded to other fuzzing processes, perhaps on the same system or perhaps even on other systems. Then again, using a file system directory as a "corpus" directory would also allow other processes, including peers and parents, to notice and pick up interesting inputs.

This issue is a tad bit open ended, but at least to close it, interesting inputs that are found should be displayed in a reproducible manner, where re-running a particular command will in fact reproduce the crash.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementSolving this issue will likely involve adding new logic or components to the codebase.fuzzing

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions