Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce LLVMFuzzerInitialize support #128

Merged
merged 12 commits into from
Jan 24, 2025
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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()
}
74 changes: 64 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,37 @@ 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;
///
/// fuzz_target!(init: (), |input| {
/// // ...
/// });
zi0Black marked this conversation as resolved.
Show resolved Hide resolved
/// ```
///
#[macro_export]
macro_rules! fuzz_target {
(|$bytes:ident| $body:expr) => {
(init: $init:expr, |$bytes:ident| $body:expr) => {
zi0Black marked this conversation as resolved.
Show resolved Hide resolved
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 +265,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 +348,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.
zi0Black marked this conversation as resolved.
Show resolved Hide resolved
#[inline(never)]
fn __libfuzzer_sys_run($data: $dty) -> $rty {
Expand Down