Skip to content

Commit

Permalink
Merge pull request #128 from zi0Black/support-for-LLVMFuzzerInitialize
Browse files Browse the repository at this point in the history
Introduce LLVMFuzzerInitialize support
  • Loading branch information
fitzgen authored Jan 24, 2025
2 parents a247317 + 55cb6cd commit bc6ea8b
Show file tree
Hide file tree
Showing 9 changed files with 154 additions and 10 deletions.
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ members = [
"./example/fuzz",
"./example_arbitrary/fuzz",
"./example_crossover/fuzz",
"./example_init/fuzz",
"./example_mutator/fuzz",
]

Expand Down
6 changes: 6 additions & 0 deletions ci/script.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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!"
1 change: 1 addition & 0 deletions example_init/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
crash-*
6 changes: 6 additions & 0 deletions example_init/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package]
name = "example_init"
version = "0.1.0"
edition = "2021"

[dependencies]
16 changes: 16 additions & 0 deletions example_init/fuzz/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
13 changes: 13 additions & 0 deletions example_init/fuzz/fuzz_targets/bigbang.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#![no_main]

use libfuzzer_sys::fuzz_target;

fuzz_target!(
init: {
// Custom initialization code here
println!("Initializing fuzzer...");
example_init::initialize();
},
|data: &[u8]| {
example_init::bigbang(data);
});
22 changes: 22 additions & 0 deletions example_init/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use std::sync::OnceLock;

static EXTRA_DATA: OnceLock<&'static str> = OnceLock::new();

pub fn bigbang(data: &[u8]) {
// The fuzzer needs to mutate input to be "bigbang!"
// Init needs to be called before bigbang() is called
// This actually proves that the fuzzer is calling init before bigbang
if data == &b"bigbang!"[..] && is_initialized() {
panic!("bigbang!");
}
}

pub fn initialize() {
EXTRA_DATA
.set("initialized")
.expect("should only initialize once");
}

pub fn is_initialized() -> bool {
EXTRA_DATA.get().is_some()
}
85 changes: 75 additions & 10 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ pub fn rust_libfuzzer_debug_path() -> &'static Option<String> {
}

#[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
Expand All @@ -89,10 +88,10 @@ pub fn initialize(_argc: *const isize, _argv: *const *const *const u8) -> isize
// 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| {
let default_hook = std::panic::take_hook();
std::panic::set_hook(Box::new(move |panic_info| {
default_hook(panic_info);
::std::process::abort();
std::process::abort();
}));
0
}
Expand Down Expand Up @@ -196,11 +195,48 @@ pub fn initialize(_argc: *const isize, _argv: *const *const *const u8) -> isize
///
/// You can also enable the `arbitrary` crate's custom derive via this crate's
/// `"arbitrary-derive"` cargo feature.
///
/// ## Init Code
///
/// Init code to the fuzz target by using the `init` keyword. This is called once before the fuzzer starts.
/// Supports short |input| or |input: <type>| syntax.
///
/// ```no_run
/// #![no_main]
///
/// use libfuzzer_sys::fuzz_target;
/// use std::collections::HashSet;
/// use std::sync::OnceLock;
///
/// static DICTIONARY: OnceLock<HashSet<String>> = OnceLock::new();
///
/// fuzz_target!(
/// init: {
/// let read_dictionary = |_| unimplemented!();
/// let dictionary = read_dictionary("/usr/share/dict/words");
/// DICTIONARY.set(dictionary).unwrap();
/// },
/// |input| {
/// // Use the initialized `DICTIONARY` here...
/// }
/// );
/// ```
///
#[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 {
$crate::initialize(_argc, _argv);

// 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
Expand Down Expand Up @@ -240,17 +276,47 @@ macro_rules! fuzz_target {
};
};

(|$bytes:ident| $body:expr) => {
$crate::fuzz_target!(|$bytes: &[u8]| $body);
};

(|$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, |$bytes:ident| $body:expr) => {
$crate::fuzz_target!(init: $init, |$bytes: &[u8]| $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 {
$crate::initialize(_argc, _argv);

// Supplied init code
$init;
0
}

#[no_mangle]
pub extern "C" fn rust_fuzzer_test_input(bytes: &[u8]) -> i32 {
use $crate::arbitrary::{Arbitrary, Unstructured};
Expand Down Expand Up @@ -293,7 +359,6 @@ macro_rules! fuzz_target {
let result = ::libfuzzer_sys::Corpus::from(__libfuzzer_sys_run(data));
result.to_libfuzzer_code()
}

// See above for why this is split to a separate function.
#[inline(never)]
fn __libfuzzer_sys_run($data: $dty) -> $rty {
Expand Down

0 comments on commit bc6ea8b

Please sign in to comment.