From d56192101b1f56da88a76220f0cefc27c2a17e13 Mon Sep 17 00:00:00 2001 From: zi0Black <13380579+zi0Black@users.noreply.github.com> Date: Sun, 22 Dec 2024 00:49:56 +0100 Subject: [PATCH] Enhance fuzzing capabilities with LLVMFuzzerInitialize support - Added `example_init` to demonstrate the use of initialization code with the `fuzz_target!` macro. - Updated `fuzz_target!` macro to support an `init` parameter for executing initialization code before fuzzing. - Updated CI script to build and run the new example. --- CHANGELOG.md | 14 ++++ Cargo.toml | 1 + ci/script.sh | 6 ++ example_init/.gitignore | 1 + example_init/Cargo.toml | 6 ++ example_init/fuzz/Cargo.toml | 16 +++++ example_init/fuzz/fuzz_targets/bigbang.rs | 16 +++++ example_init/src/lib.rs | 5 ++ src/lib.rs | 86 +++++++++++++++++------ 9 files changed, 128 insertions(+), 23 deletions(-) create mode 100644 example_init/.gitignore create mode 100644 example_init/Cargo.toml create mode 100644 example_init/fuzz/Cargo.toml create mode 100755 example_init/fuzz/fuzz_targets/bigbang.rs create mode 100644 example_init/src/lib.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index ab111ff..e089dab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,20 @@ Released YYYY-MM-DD. -------------------------------------------------------------------------------- +## 0.4.9 + +Released YYYY-MM-DD. + +### Added + +* The `example_init` demonstrates how to pass an initialization code block to the `fuzz_target!` macro. + +### Changed + +* The `fuzz_target!` macro now supports the generation of `LLVMFuzzerInitialize` to execute initialization code once before running the fuzzer. This change is not breaking and is completely backward compatible. + +-------------------------------------------------------------------------------- + ## 0.4.8 Released 2024-11-07. diff --git a/Cargo.toml b/Cargo.toml index beaf4c5..27cae5b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ members = [ "./example/fuzz", "./example_arbitrary/fuzz", "./example_crossover/fuzz", + "./example_init/fuzz", "./example_mutator/fuzz", ] diff --git a/ci/script.sh b/ci/script.sh index 3eb2d17..782fd04 100755 --- a/ci/script.sh +++ b/ci/script.sh @@ -36,4 +36,10 @@ cargo fuzz build --dev (! cargo fuzz run --release boom -- -runs=10000000) popd +pushd ./example_init +cargo fuzz build +cargo fuzz build --dev +(! cargo fuzz run --release bigbang -- -runs=10000000) +popd + echo "All good!" diff --git a/example_init/.gitignore b/example_init/.gitignore new file mode 100644 index 0000000..d8d1df6 --- /dev/null +++ b/example_init/.gitignore @@ -0,0 +1 @@ +crash-* diff --git a/example_init/Cargo.toml b/example_init/Cargo.toml new file mode 100644 index 0000000..74bd477 --- /dev/null +++ b/example_init/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "example_init" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/example_init/fuzz/Cargo.toml b/example_init/fuzz/Cargo.toml new file mode 100644 index 0000000..2ea16b0 --- /dev/null +++ b/example_init/fuzz/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "example_init-fuzz" +version = "0.1.0" +authors = ["Andrea Cappa"] +edition = "2018" + +[package.metadata] +cargo-fuzz = true + +[dependencies] +libfuzzer-sys = { path = "../.." } +example_init = { path = ".." } + +[[bin]] +name = "bigbang" +path = "fuzz_targets/bigbang.rs" diff --git a/example_init/fuzz/fuzz_targets/bigbang.rs b/example_init/fuzz/fuzz_targets/bigbang.rs new file mode 100755 index 0000000..a05d25c --- /dev/null +++ b/example_init/fuzz/fuzz_targets/bigbang.rs @@ -0,0 +1,16 @@ +#![no_main] + +use libfuzzer_sys::fuzz_target; + +fuzz_target!( + init: { + // Custom initialization code here + println!("Initializing fuzzer..."); + std::env::set_var("MY_FUZZER_INIT", "1337"); + }, + |data: &[u8]| { + if std::env::var("MY_FUZZER_INIT").unwrap() == "1337" && data == "bigbang!".as_bytes() { + panic!("success!"); + } + example_init::bigbang(data); +}); diff --git a/example_init/src/lib.rs b/example_init/src/lib.rs new file mode 100644 index 0000000..f91c934 --- /dev/null +++ b/example_init/src/lib.rs @@ -0,0 +1,5 @@ +pub fn bigbang(data: &[u8]) { + if data == &b"bigbang!"[..] { + panic!("bigbang!"); + } +} diff --git a/src/lib.rs b/src/lib.rs index 8df0e5c..e8142e5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -78,24 +78,11 @@ pub fn rust_libfuzzer_debug_path() -> &'static Option { RUST_LIBFUZZER_DEBUG_PATH.get_or_init(|| std::env::var("RUST_LIBFUZZER_DEBUG_PATH").ok()) } -#[doc(hidden)] +/* #[doc(hidden)] #[export_name = "LLVMFuzzerInitialize"] pub fn initialize(_argc: *const isize, _argv: *const *const *const u8) -> isize { - // Registers a panic hook that aborts the process before unwinding. - // It is useful to abort before unwinding so that the fuzzer will then be - // able to analyse the process stack frames to tell different bugs appart. - // - // HACK / FIXME: it would be better to use `-C panic=abort` but it's currently - // impossible to build code using compiler plugins with this flag. - // We will be able to remove this code when - // https://github.com/rust-lang/cargo/issues/5423 is fixed. - let default_hook = ::std::panic::take_hook(); - ::std::panic::set_hook(Box::new(move |panic_info| { - default_hook(panic_info); - ::std::process::abort(); - })); 0 -} +} */ /// Define a fuzz target. /// @@ -198,9 +185,31 @@ pub fn initialize(_argc: *const isize, _argv: *const *const *const u8) -> isize /// `"arbitrary-derive"` cargo feature. #[macro_export] macro_rules! fuzz_target { - (|$bytes:ident| $body:expr) => { + (init: $init:expr, |$bytes:ident| $body:expr) => { const _: () = { - /// Auto-generated function + /// Auto-generated functions + /// LLVMFuzzerInitialize is called once before the fuzzer starts. + #[no_mangle] + pub extern "C" fn LLVMFuzzerInitialize(_argc: *const isize, _argv: *const *const *const u8) -> isize { + // Registers a panic hook that aborts the process before unwinding. + // It is useful to abort before unwinding so that the fuzzer will then be + // able to analyse the process stack frames to tell different bugs appart. + // + // HACK / FIXME: it would be better to use `-C panic=abort` but it's currently + // impossible to build code using compiler plugins with this flag. + // We will be able to remove this code when + // https://github.com/rust-lang/cargo/issues/5423 is fixed. + let default_hook = std::panic::take_hook(); + std::panic::set_hook(Box::new(move |panic_info| { + default_hook(panic_info); + std::process::abort(); + })); + + // Supplied init code + $init; + 0 + } + #[no_mangle] pub extern "C" fn rust_fuzzer_test_input(bytes: &[u8]) -> i32 { // When `RUST_LIBFUZZER_DEBUG_PATH` is set, write the debug @@ -241,16 +250,50 @@ macro_rules! fuzz_target { }; (|$data:ident: &[u8]| $body:expr) => { - $crate::fuzz_target!(|$data| $body); + $crate::fuzz_target!(init: (), |$data| $body); }; (|$data:ident: $dty:ty| $body:expr) => { - $crate::fuzz_target!(|$data: $dty| -> () { $body }); + $crate::fuzz_target!(init: (), |$data: $dty| -> () { $body }); }; (|$data:ident: $dty:ty| -> $rty:ty $body:block) => { + $crate::fuzz_target!(init: (), |$data: $dty| -> $rty { $body }); + }; + + (init: $init:expr, |$data:ident: &[u8]| $body:expr) => { + $crate::fuzz_target!(init: $init, |$data| $body); + }; + + (init: $init:expr, |$data:ident: $dty:ty| $body:expr) => { + $crate::fuzz_target!(init: $init, |$data: $dty| -> () { $body }); + }; + + (init: $init:expr, |$data:ident: $dty:ty| -> $rty:ty $body:block) => { const _: () = { - /// Auto-generated function + /// Auto-generated functions + /// LLVMFuzzerInitialize is called once before the fuzzer starts. + #[no_mangle] + pub extern "C" fn LLVMFuzzerInitialize(_argc: *const isize, _argv: *const *const *const u8) -> isize { + // Registers a panic hook that aborts the process before unwinding. + // It is useful to abort before unwinding so that the fuzzer will then be + // able to analyse the process stack frames to tell different bugs appart. + // + // HACK / FIXME: it would be better to use `-C panic=abort` but it's currently + // impossible to build code using compiler plugins with this flag. + // We will be able to remove this code when + // https://github.com/rust-lang/cargo/issues/5423 is fixed. + let default_hook = std::panic::take_hook(); + std::panic::set_hook(Box::new(move |panic_info| { + default_hook(panic_info); + std::process::abort(); + })); + + // Supplied init code + $init; + 0 + } + #[no_mangle] pub extern "C" fn rust_fuzzer_test_input(bytes: &[u8]) -> i32 { use $crate::arbitrary::{Arbitrary, Unstructured}; @@ -271,8 +314,6 @@ macro_rules! fuzz_target { // When `RUST_LIBFUZZER_DEBUG_PATH` is set, write the debug // formatting of the input to that file. This is only intended for // `cargo fuzz`'s use! - - // `RUST_LIBFUZZER_DEBUG_PATH` is set in initialization. if let Some(path) = $crate::rust_libfuzzer_debug_path() { use std::io::Write; let mut file = std::fs::File::create(path) @@ -294,7 +335,6 @@ macro_rules! fuzz_target { result.to_libfuzzer_code() } - // See above for why this is split to a separate function. #[inline(never)] fn __libfuzzer_sys_run($data: $dty) -> $rty { $body