Skip to content

Add a fuzz! macro that can be called from main - #146

Open
chris-srlabs wants to merge 1 commit into
rust-fuzz:mainfrom
chris-srlabs:feat/fuzz-macro
Open

chris-srlabs wants to merge 1 commit into
rust-fuzz:mainfrom
chris-srlabs:feat/fuzz-macro

Conversation

@chris-srlabs

Copy link
Copy Markdown

This adds a new fuzz! macro that lets you call libFuzzer from main, run setup beforehand, and use local state in the fuzz closure (see #33 for a previous attempt in that direction). The goal is to make it easier to switch to afl.rs or honggfuzz and back to libfuzzer-sys (see the discussion in rust-fuzz/cargo-fuzz#119). This is only a first step in that direction.

The new macro is gated behind the fuzz_macro feature because it adds a libc dependency on Unix. It invokes libFuzzer through LLVMFuzzerRunDriver.

Since the closure passed to fuzz! can capture its environment, its lifetime needs careful handling. A trampoline connects libFuzzer's callback to the closure.

Signal handling also needs care because Rust's signal handlers can interfere with libFuzzer's (now that the target has a Rust main function).

Macro hygiene in the existing fuzz_target! macro is also improved, allowing downstream crates to rename the libfuzzer-sys dependency.


The signal-matrix tool was written entirely by AI (kept outside this PR on a separate branch). But it does the job of comparing how the existing fuzz_target! harness and the new fuzz! harness called from main handle different crash signals. It compares those harnesses with ASan and without a sanitizer.
On my linux system it outputs this divergence matrix (exec via cargo run -p signal-matrix):

crash sanitizer fuzz_target! (no_main) fuzz! (main) diff
panic none saved (deadly signal) saved (deadly signal)
abort none saved (deadly signal) saved (deadly signal)
segv none saved (deadly signal) saved (deadly signal)
bus none saved (deadly signal) saved (deadly signal)
ill none saved (deadly signal) saved (deadly signal)
fpe none saved (deadly signal) saved (deadly signal)
trap none saved (deadly signal) saved (deadly signal)
stack-overflow none MISSED (SIGSEGV) MISSED (SIGSEGV)
timeout none saved (timeout) saved (timeout)
sigpipe none MISSED (SIGPIPE) saved (deadly signal) differs
panic address saved (deadly signal) saved (deadly signal)
abort address saved (deadly signal) saved (deadly signal)
segv address saved (ASan SEGV) saved (ASan SEGV)
bus address saved (ASan BUS) saved (ASan BUS)
ill address saved (deadly signal) saved (deadly signal)
fpe address saved (ASan FPE) saved (ASan FPE)
trap address saved (deadly signal) saved (deadly signal)
stack-overflow address saved (ASan stack-overflow) saved (ASan stack-overflow)
timeout address saved (timeout) saved (timeout)
sigpipe address MISSED (SIGPIPE) saved (deadly signal) differs

The new `fuzz!` macro does not require `#[no_main]` and can be called
directly from `main`. It invokes libFuzzer through
`LLVMFuzzerRunDriver`.
Since the closure passed to `fuzz!` can capture its environment, its
lifetime needs careful handling. A trampoline connects libFuzzer's
callback to the closure.
Signal handling also needs care because Rust's signal handlers can
interfere with libFuzzer's.
Macro hygiene in the existing `fuzz_target!` macro is also improved,
allowing the libfuzzer-sys crate to be renamed.
The new `fuzz!` macro is gated behind the `fuzz_macro` feature as it
adds the libc dependency on unix.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant