From bacd235cb388f70e814209013070a4cbf31a7dc3 Mon Sep 17 00:00:00 2001 From: Rowan Hart Date: Fri, 4 Apr 2025 12:02:13 -0700 Subject: [PATCH 01/22] Add windows build script and additional changes to support windows for libafl-libfuzzer --- libafl_libfuzzer/runtime/Cargo.toml.template | 10 ++- libafl_libfuzzer/runtime/src/fuzz.rs | 91 +++++++++++++++----- libafl_libfuzzer/runtime/src/lib.rs | 5 +- libafl_libfuzzer/runtime/src/merge.rs | 3 +- libafl_libfuzzer/runtime/src/options.rs | 4 + libafl_libfuzzer/runtime/src/tmin.rs | 17 +++- libafl_libfuzzer_runtime/build.ps1 | 83 ++++++++++++++++++ 7 files changed, 184 insertions(+), 29 deletions(-) create mode 100644 libafl_libfuzzer_runtime/build.ps1 diff --git a/libafl_libfuzzer/runtime/Cargo.toml.template b/libafl_libfuzzer/runtime/Cargo.toml.template index 55fcb5a902b..81621d35859 100644 --- a/libafl_libfuzzer/runtime/Cargo.toml.template +++ b/libafl_libfuzzer/runtime/Cargo.toml.template @@ -4,16 +4,19 @@ version = "0.15.2" edition = "2024" publish = false -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [features] -default = ["fork"] +default = [] ## Enables forking mode for the LibAFL launcher (instead of starting new processes) fork = ["libafl/fork"] track_hit_feedbacks = [ "libafl/track_hit_feedbacks", "libafl_targets/track_hit_feedbacks", ] +tui_monitor = ["libafl/tui_monitor"] + +[target.'cfg(not(windows))'.features] +## Enable the `fork` feature on non-windows platforms +default = ["fork", "tui_monitor"] [profile.release] lto = true @@ -40,7 +43,6 @@ libafl = { path = "../libafl", default-features = false, features = [ "regex", "errors_backtrace", "serdeany_autoreg", - "tui_monitor", "unicode", ] } libafl_bolts = { path = "../libafl_bolts", default-features = false, features = [ diff --git a/libafl_libfuzzer/runtime/src/fuzz.rs b/libafl_libfuzzer/runtime/src/fuzz.rs index 8a1d2a43e04..ffea7e4a2b9 100644 --- a/libafl_libfuzzer/runtime/src/fuzz.rs +++ b/libafl_libfuzzer/runtime/src/fuzz.rs @@ -1,8 +1,10 @@ use core::ffi::c_int; #[cfg(unix)] -use std::io::{Write, stderr, stdout}; -use std::{fmt::Debug, fs::File, net::TcpListener, os::fd::AsRawFd, str::FromStr}; +use std::io::{Write, os::fd::AsRawFd, stderr, stdout}; +use std::{fmt::Debug, fs::File, net::TcpListener, str::FromStr}; +#[cfg(feature = "tui_monitor")] +use libafl::monitors::tui::TuiMonitor; use libafl::{ Error, Fuzzer, HasMetadata, corpus::Corpus, @@ -11,7 +13,7 @@ use libafl::{ SimpleRestartingEventManager, launcher::Launcher, }, executors::ExitKind, - monitors::{Monitor, MultiMonitor, tui::TuiMonitor}, + monitors::{Monitor, MultiMonitor}, stages::StagesTuple, state::{HasCurrentStageId, HasExecutions, HasLastReportTime, HasSolutions, Stoppable}, }; @@ -31,10 +33,14 @@ fn destroy_output_fds(options: &LibfuzzerOptions) { let stdout_fd = stdout().as_raw_fd(); let stderr_fd = stderr().as_raw_fd(); + #[cfg(feature = "tui_monitor")] if options.tui() { dup2(null_fd, stdout_fd).unwrap(); dup2(null_fd, stderr_fd).unwrap(); - } else if options.close_fd_mask() != 0 { + return; + } + + if options.close_fd_mask() != 0 { if options.close_fd_mask() & u8::try_from(stderr_fd).unwrap() != 0 { dup2(null_fd, stdout_fd).unwrap(); } @@ -91,6 +97,7 @@ where Ok(()) } +#[cfg(unix)] fn fuzz_single_forking( options: &LibfuzzerOptions, harness: &extern "C" fn(*const u8, usize) -> c_int, @@ -121,9 +128,23 @@ where }) } +#[cfg(windows)] +fn fuzz_single_forking( + options: &LibfuzzerOptions, + harness: &extern "C" fn(*const u8, usize) -> c_int, + shmem_provider: StdShMemProvider, + monitor: M, +) -> Result<(), Error> +where + M: Monitor + Debug, +{ + panic!("Forking not supported on Windows"); +} + /// Communicate the selected port to subprocesses const PORT_PROVIDER_VAR: &str = "_LIBAFL_LIBFUZZER_FORK_PORT"; +#[cfg(unix)] fn fuzz_many_forking( options: &LibfuzzerOptions, harness: &extern "C" fn(*const u8, usize) -> c_int, @@ -170,6 +191,20 @@ where }) } +#[cfg(windows)] +fn fuzz_many_forking( + options: &LibfuzzerOptions, + harness: &extern "C" fn(*const u8, usize) -> c_int, + shmem_provider: StdShMemProvider, + forks: usize, + monitor: M, +) -> Result<(), Error> +where + M: Monitor + Clone + Debug + 'static, +{ + panic!("Forking not supported on Windows"); +} + fn create_monitor_closure() -> impl Fn(&str) + Clone { #[cfg(unix)] let stderr_fd = @@ -196,29 +231,41 @@ pub fn fuzz( ) -> Result<(), Error> { if let Some(forks) = options.forks() { let shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory"); - if options.tui() { - let monitor = TuiMonitor::builder() - .title(options.fuzzer_name()) - .enhanced_graphics(true) - .build(); - fuzz_many_forking(options, harness, shmem_provider, forks, monitor) - } else if forks == 1 { - let monitor = MultiMonitor::new(create_monitor_closure()); + + #[cfg(feature = "tui_monitor")] + { + if options.tui() { + let monitor = TuiMonitor::builder() + .title(options.fuzzer_name()) + .enhanced_graphics(true) + .build(); + return fuzz_many_forking(options, harness, shmem_provider, forks, monitor); + } + } + + // Non-TUI path or when tui_monitor feature is disabled + let monitor = MultiMonitor::new(create_monitor_closure()); + if forks == 1 { fuzz_single_forking(options, harness, shmem_provider, monitor) } else { - let monitor = MultiMonitor::new(create_monitor_closure()); fuzz_many_forking(options, harness, shmem_provider, forks, monitor) } - } else if options.tui() { - // if the user specifies TUI, we assume they want to fork; it would not be possible to use - // TUI safely otherwise - let shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory"); - let monitor = TuiMonitor::builder() - .title(options.fuzzer_name()) - .enhanced_graphics(true) - .build(); - fuzz_many_forking(options, harness, shmem_provider, 1, monitor) } else { + #[cfg(feature = "tui_monitor")] + { + if options.tui() { + // if the user specifies TUI, we assume they want to fork; it would not be possible to use + // TUI safely otherwise + let shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory"); + let monitor = TuiMonitor::builder() + .title(options.fuzzer_name()) + .enhanced_graphics(true) + .build(); + return fuzz_many_forking(options, harness, shmem_provider, 1, monitor); + } + } + + // Default path when no forks or TUI are specified, or when tui_monitor feature is disabled destroy_output_fds(options); fuzz_with!(options, harness, do_fuzz, |fuzz_single| { let mgr = SimpleEventManager::new(MultiMonitor::new(create_monitor_closure())); diff --git a/libafl_libfuzzer/runtime/src/lib.rs b/libafl_libfuzzer/runtime/src/lib.rs index da826008aea..ce50c02db71 100644 --- a/libafl_libfuzzer/runtime/src/lib.rs +++ b/libafl_libfuzzer/runtime/src/lib.rs @@ -67,7 +67,10 @@ #![allow(clippy::borrow_deref_ref)] use core::ffi::{CStr, c_char, c_int}; -use std::{fs::File, io::stderr, os::fd::RawFd}; +use std::{fs::File, io::stderr}; + +#[cfg(unix)] +use std::os::fd::RawFd; use env_logger::Target; use libafl::{ diff --git a/libafl_libfuzzer/runtime/src/merge.rs b/libafl_libfuzzer/runtime/src/merge.rs index ac3ffcea6f0..5b0f6f0ddd1 100644 --- a/libafl_libfuzzer/runtime/src/merge.rs +++ b/libafl_libfuzzer/runtime/src/merge.rs @@ -1,9 +1,10 @@ +#[cfg(unix)] +use std::os::fd::{AsRawFd, FromRawFd}; use std::{ env::temp_dir, ffi::c_int, fs::{File, rename}, io::Write, - os::fd::{AsRawFd, FromRawFd}, }; use libafl::{ diff --git a/libafl_libfuzzer/runtime/src/options.rs b/libafl_libfuzzer/runtime/src/options.rs index 8a487c20219..3065522a06a 100644 --- a/libafl_libfuzzer/runtime/src/options.rs +++ b/libafl_libfuzzer/runtime/src/options.rs @@ -320,6 +320,10 @@ impl<'a> LibfuzzerOptionsBuilder<'a> { "dict" => self.dict = Some(value), "fork" | "jobs" => { self.forks = Some(parse_or_bail!(name, value, usize)); + #[cfg(windows)] + if self.forks.unwrap() > 1 { + panic!("Error: Windows does not support forking!"); + } } "ignore_crashes" => { self.ignore_crashes = Some(parse_or_bail!(name, value, u64) > 0); diff --git a/libafl_libfuzzer/runtime/src/tmin.rs b/libafl_libfuzzer/runtime/src/tmin.rs index 7afb05d214d..9cee0b1b345 100644 --- a/libafl_libfuzzer/runtime/src/tmin.rs +++ b/libafl_libfuzzer/runtime/src/tmin.rs @@ -3,11 +3,15 @@ use std::{ fs::{read, write}, }; +#[cfg(windows)] +use libafl::executors::inprocess::InProcessExecutor; +#[cfg(unix)] +use libafl::executors::inprocess_fork::InProcessForkExecutor; use libafl::{ Error, ExecutesInput, Fuzzer, StdFuzzer, corpus::{Corpus, HasTestcase, InMemoryCorpus, Testcase}, events::SimpleEventManager, - executors::{ExitKind, inprocess_fork::InProcessForkExecutor}, + executors::ExitKind, feedbacks::{CrashFeedback, TimeoutFeedback}, inputs::{BytesInput, HasMutatorBytes, HasTargetBytes}, mutators::{Mutator, StdScheduledMutator, havoc_mutations_no_crossover}, @@ -62,6 +66,7 @@ fn minimize_crash_with_mutator>( let mut fuzzer = StdFuzzer::new(QueueScheduler::new(), (), ()); let shmem_provider = StdShMemProvider::new()?; + #[cfg(unix)] let mut executor = InProcessForkExecutor::new( &mut harness, (), @@ -72,6 +77,16 @@ fn minimize_crash_with_mutator>( shmem_provider, )?; + #[cfg(windows)] + let mut executor = InProcessExecutor::with_timeout( + &mut harness, + (), + &mut fuzzer, + &mut state, + &mut mgr, + options.timeout(), + )?; + let exit_kind = fuzzer.execute_input(&mut state, &mut executor, &mut mgr, &input)?; let size = input.len(); diff --git a/libafl_libfuzzer_runtime/build.ps1 b/libafl_libfuzzer_runtime/build.ps1 new file mode 100644 index 00000000000..86b01f39150 --- /dev/null +++ b/libafl_libfuzzer_runtime/build.ps1 @@ -0,0 +1,83 @@ +#!/usr/bin/env pwsh + +$ErrorActionPreference = "Stop" + +$SCRIPT_DIR = Split-Path -Parent $MyInvocation.MyCommand.Path + +Set-Location $SCRIPT_DIR + +if ($args.Count -eq 0) { + $profile = "release" +} else { + $profile = $args[0] +} + +try { + $nightly_version = Invoke-Expression "cargo +nightly --version" 2>$null + if (-not $nightly_version) { + Write-Host "You must install a recent Rust nightly to build the libafl_libfuzzer runtime!" -ForegroundColor Red + exit 1 + } +} catch { + Write-Host "You must install a recent Rust nightly to build the libafl_libfuzzer runtime!" -ForegroundColor Red + exit 1 +} + +Write-Host "Building libafl_libfuzzer runtime with profile '$profile'" -ForegroundColor Green +Invoke-Expression "cargo +nightly build --profile $profile" + +# target-libdir is e.g. C:\Users\user\.rustup\toolchain\nightly-x86_64-pc-windows-msvc\lib\rustlib\x86_64-pc-windows-msvc\lib +$RUSTC_BIN = Split-Path -Parent (Invoke-Expression "cargo +nightly rustc -Zunstable-options --print target-libdir") +$RUSTC_BIN = Join-Path $RUSTC_BIN "bin" +$RUST_LLD = Join-Path $RUSTC_BIN "rust-lld.exe" +$RUST_AR = Join-Path $RUSTC_BIN "llvm-ar.exe" +$RUST_NM = Join-Path $RUSTC_BIN "llvm-nm.exe" + +if (-not (Test-Path $RUST_LLD) -or -not (Test-Path $RUST_AR)) { + Write-Host "You must install the llvm-tools component: 'rustup component add llvm-tools'" -ForegroundColor Red + Write-Host "Could not find $RUST_LLD or $RUST_AR" -ForegroundColor Red + exit 1 +} + +$tmpdir = Join-Path $env:TEMP ([System.IO.Path]::GetRandomFileName()) +New-Item -ItemType Directory -Path $tmpdir | Out-Null + +function Cleanup { + if (Test-Path $tmpdir) { + Remove-Item -Recurse -Force $tmpdir + } +} + +try { + $targetPath = Join-Path $SCRIPT_DIR "target\$profile\afl_libfuzzer_runtime.lib" + $outputPath = Join-Path $SCRIPT_DIR "libFuzzer.lib" + + Write-Host "Creating intermediate object file '$tmpdir\libFuzzer.obj from $targetPath'" -ForegroundColor Green + & $RUST_LLD -flavor link /lib /nologo /out:"$tmpdir\libFuzzer.obj" "$targetPath" + + if ($LASTEXITCODE -ne 0) { + throw "Failed to create intermediate object file" + } + + Write-Host "Creating final library '$outputPath'" -ForegroundColor Green + & $RUST_AR crs "$outputPath" "$tmpdir\libFuzzer.obj" + + if ($LASTEXITCODE -ne 0) { + throw "Failed to create final library" + } + + Write-Host "Verifying symbols from '$outputPath'" -ForegroundColor Green + # Symbols that should be present: + # LLVMFuzzerRunDriver + & $RUST_NM "$outputPath" | Select-String "LLVMFuzzerRunDriver" | Out-Null + + if ($LASTEXITCODE -ne 0) { + throw "Failed to verify symbols in final library" + } + + Write-Host "Done! Wrote the runtime to '$outputPath'" -ForegroundColor Green + +} +finally { + Cleanup +} \ No newline at end of file From 6a10ca67fb9daa73194e4a0db1e2fcbee1d09d55 Mon Sep 17 00:00:00 2001 From: Rowan Hart Date: Fri, 4 Apr 2025 15:56:53 -0700 Subject: [PATCH 02/22] Update build scripts and harness wrapping directives --- libafl_libfuzzer/runtime/src/harness_wrap.cpp | 13 +++++++++++++ libafl_libfuzzer_runtime/build.ps1 | 6 ++++++ libafl_libfuzzer_runtime/build.rs | 9 +++++---- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/libafl_libfuzzer/runtime/src/harness_wrap.cpp b/libafl_libfuzzer/runtime/src/harness_wrap.cpp index 21e3b625407..134aaefcc9c 100644 --- a/libafl_libfuzzer/runtime/src/harness_wrap.cpp +++ b/libafl_libfuzzer/runtime/src/harness_wrap.cpp @@ -8,3 +8,16 @@ extern "C" int libafl_libfuzzer_test_one_input( return -2; // custom code for "we died!" } } + +#ifdef _WIN32 +#pragma comment(lib, "advapi32.lib") +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "userenv.lib") +#pragma comment(lib, "ntdll.lib") +#pragma comment(lib, "bcrypt.lib") +#pragma comment(lib, "ole32.lib") +#pragma comment(lib, "dbghelp.lib") + +#pragma comment(linker, "/export:LLVMFuzzerRunDriver") +#pragma comment(linker, "/export:__sanitizer_cov_8bit_counters_init") +#endif // _WIN32 diff --git a/libafl_libfuzzer_runtime/build.ps1 b/libafl_libfuzzer_runtime/build.ps1 index 86b01f39150..00bc2589791 100644 --- a/libafl_libfuzzer_runtime/build.ps1 +++ b/libafl_libfuzzer_runtime/build.ps1 @@ -49,6 +49,12 @@ function Cleanup { } try { + if ($profile -eq "dev") { + # Set the profile to debug for dev builds, because the path isn't the same + # as the profile name + $profile = "debug" + } + $targetPath = Join-Path $SCRIPT_DIR "target\$profile\afl_libfuzzer_runtime.lib" $outputPath = Join-Path $SCRIPT_DIR "libFuzzer.lib" diff --git a/libafl_libfuzzer_runtime/build.rs b/libafl_libfuzzer_runtime/build.rs index cfffcfc9c73..8590e4b7ebb 100644 --- a/libafl_libfuzzer_runtime/build.rs +++ b/libafl_libfuzzer_runtime/build.rs @@ -17,8 +17,9 @@ fn main() { .write_to_file(Path::new(&out_dir).join("harness_wrap.rs")) .expect("Couldn't write the harness wrapper!"); - cc::Build::new() - .cpp(true) - .file("src/harness_wrap.cpp") - .compile("harness_wrap"); + let mut harness_wrap = cc::Build::new(); + + harness_wrap.cpp(true).file("src/harness_wrap.cpp"); + + harness_wrap.compile("harness_wrap"); } From 839e422476996dce1c320852986cd64b4476b221 Mon Sep 17 00:00:00 2001 From: Rowan Hart Date: Mon, 7 Apr 2025 08:02:05 -0700 Subject: [PATCH 03/22] Resolve issue with corpus edge count calculation --- libafl_libfuzzer/runtime/src/corpus.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libafl_libfuzzer/runtime/src/corpus.rs b/libafl_libfuzzer/runtime/src/corpus.rs index f9ba42ebb4c..e7153e76b13 100644 --- a/libafl_libfuzzer/runtime/src/corpus.rs +++ b/libafl_libfuzzer/runtime/src/corpus.rs @@ -154,7 +154,7 @@ where } #[inline] fn count_all(&self) -> usize { - self.count_disabled().saturating_add(self.count_disabled()) + self.count().saturating_add(self.count_disabled()) } #[expect(clippy::used_underscore_items)] From 7a923b1c41b3d72288892f29882f820b4a1541ce Mon Sep 17 00:00:00 2001 From: Rowan Hart Date: Mon, 7 Apr 2025 09:02:40 -0700 Subject: [PATCH 04/22] Add help message and make fork do nothing on Windows --- libafl_libfuzzer/runtime/Cargo.toml.template | 4 +- libafl_libfuzzer/runtime/src/harness_wrap.cpp | 9 ++ libafl_libfuzzer/runtime/src/options.rs | 96 ++++++++++++++++++- 3 files changed, 103 insertions(+), 6 deletions(-) diff --git a/libafl_libfuzzer/runtime/Cargo.toml.template b/libafl_libfuzzer/runtime/Cargo.toml.template index 81621d35859..58f8d2fa0df 100644 --- a/libafl_libfuzzer/runtime/Cargo.toml.template +++ b/libafl_libfuzzer/runtime/Cargo.toml.template @@ -5,7 +5,7 @@ edition = "2024" publish = false [features] -default = [] +default = ["tui_monitor"] ## Enables forking mode for the LibAFL launcher (instead of starting new processes) fork = ["libafl/fork"] track_hit_feedbacks = [ @@ -16,7 +16,7 @@ tui_monitor = ["libafl/tui_monitor"] [target.'cfg(not(windows))'.features] ## Enable the `fork` feature on non-windows platforms -default = ["fork", "tui_monitor"] +default = ["fork"] [profile.release] lto = true diff --git a/libafl_libfuzzer/runtime/src/harness_wrap.cpp b/libafl_libfuzzer/runtime/src/harness_wrap.cpp index 134aaefcc9c..b0429542b5c 100644 --- a/libafl_libfuzzer/runtime/src/harness_wrap.cpp +++ b/libafl_libfuzzer/runtime/src/harness_wrap.cpp @@ -10,13 +10,22 @@ extern "C" int libafl_libfuzzer_test_one_input( } #ifdef _WIN32 +// For Windows API functions used by MiMalloc #pragma comment(lib, "advapi32.lib") +// For Windows networking functionality used by LibAFL #pragma comment(lib, "ws2_32.lib") +// For Windows API functions to retrieve user home directory used by Rust STD #pragma comment(lib, "userenv.lib") +// For base Windows API functions like file reads and writes #pragma comment(lib, "ntdll.lib") +// For crypto functions called by LibAFL's random utilities #pragma comment(lib, "bcrypt.lib") +// Required by windows_core #pragma comment(lib, "ole32.lib") +// For debug facilities used in debug builds #pragma comment(lib, "dbghelp.lib") +// For terminal controls used by crossterm +#pragma comment(lib, "user32.lib") #pragma comment(linker, "/export:LLVMFuzzerRunDriver") #pragma comment(linker, "/export:__sanitizer_cov_8bit_counters_init") diff --git a/libafl_libfuzzer/runtime/src/options.rs b/libafl_libfuzzer/runtime/src/options.rs index 3065522a06a..a6031338b66 100644 --- a/libafl_libfuzzer/runtime/src/options.rs +++ b/libafl_libfuzzer/runtime/src/options.rs @@ -318,12 +318,9 @@ impl<'a> LibfuzzerOptionsBuilder<'a> { })?); } "dict" => self.dict = Some(value), + #[cfg(not(windows))] "fork" | "jobs" => { self.forks = Some(parse_or_bail!(name, value, usize)); - #[cfg(windows)] - if self.forks.unwrap() > 1 { - panic!("Error: Windows does not support forking!"); - } } "ignore_crashes" => { self.ignore_crashes = Some(parse_or_bail!(name, value, u64) > 0); @@ -362,6 +359,97 @@ impl<'a> LibfuzzerOptionsBuilder<'a> { } "runs" => self.runs = parse_or_bail!(name, value, usize), "close_fd_mask" => self.close_fd_mask = parse_or_bail!(name, value, u8), + "help" => { + println!("Usage:"); + println!(); + println!("To run fuzzing pass 0 or more directories."); + println!("{name} [-flag1=val1 [-flag2=val2 ...] ] [dir1 [dir2 ...] ]"); + println!(); + println!( + "To run individual tests without fuzzing pass 1 or more files:" + ); + println!("{name} [-flag1=val1 [-flag2=val2 ...] ] file1 [file2 ...]"); + println!(); + println!("Flags: (strictly in form -flag=value)"); + println!( + " artifact_prefix 0 Write fuzzing artifacts (crash, timeout, or slow inputs) as $(artifact_prefix)file" + ); + println!( + " timeout 1200 Timeout in seconds. If one unit runs more than this number of seconds the process will abort." + ); + println!( + " grimoire 0 If 1, enable the Grimoire mutator that is structure-aware." + ); + println!( + " use_value_profile 0 Use value profile to guide fuzzing." + ); + println!( + " unicode 1 If 1, generate Unicode inputs." + ); + println!( + " dict 0 Use the dictionary file." + ); + #[cfg(not(windows))] + println!( + " fork 0 Number of forks to use (>1 requires Unix-like OS)." + ); + println!( + " jobs 0 Same as fork. Number of jobs to run with stdout/stderr redirected." + ); + println!( + " ignore_crashes 0 If 1, ignore crashes in fork mode." + ); + println!( + " ignore_timeouts 0 If 1, ignore timeouts in fork mode." + ); + println!( + " ignore_ooms 0 If 1, ignore out-of-memory errors in fork mode." + ); + println!( + " rss_limit_mb 2048 If non-zero, the fuzzer will exit upon reaching this limit of RSS memory usage (in Mb)." + ); + println!( + " malloc_limit_mb 2048 If non-zero, the fuzzer will exit if the target tries to allocate this number of Mb with one malloc call." + ); + println!( + " ignore_remaining_args 0 If 1, ignore all arguments passed after this one." + ); + println!( + " dedup 0 If 1, deduplicate corpus elements." + ); + println!( + " shrink 0 If 1, try to shrink corpus elements." + ); + println!( + " skip_tracing 0 If 1, skip coverage tracing for faster execution." + ); + println!( + " tui 0 If 1, use the terminal UI interface." + ); + println!( + " runs 0 Number of individual test runs (0 for infinite runs)." + ); + println!( + " close_fd_mask 0 If 1, close stdout; if 2, close stderr; if 3, close both." + ); + println!( + " merge 0 If 1, merge multiple corpora into a single one." + ); + println!( + " minimize_crash 0 If 1, minimize crashes to their smallest reproducing input." + ); + println!( + " report 0 If 1, report statistics without actually fuzzing." + ); + println!( + " help 0 Print this help message." + ); + println!(); + println!( + "Flags starting with '--' will be ignored and will be passed verbatim to subprocesses." + ); + std::process::exit(0); + } _ => { self.unknown.push(arg); } From d8e24724854753fc3fdf83d4c06a0a50dde67dc8 Mon Sep 17 00:00:00 2001 From: Rowan Hart Date: Mon, 7 Apr 2025 09:57:08 -0700 Subject: [PATCH 05/22] Format harness_wrap.cpp --- libafl_libfuzzer/runtime/src/harness_wrap.cpp | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/libafl_libfuzzer/runtime/src/harness_wrap.cpp b/libafl_libfuzzer/runtime/src/harness_wrap.cpp index b0429542b5c..1de2ac33dec 100644 --- a/libafl_libfuzzer/runtime/src/harness_wrap.cpp +++ b/libafl_libfuzzer/runtime/src/harness_wrap.cpp @@ -10,23 +10,23 @@ extern "C" int libafl_libfuzzer_test_one_input( } #ifdef _WIN32 -// For Windows API functions used by MiMalloc -#pragma comment(lib, "advapi32.lib") -// For Windows networking functionality used by LibAFL -#pragma comment(lib, "ws2_32.lib") -// For Windows API functions to retrieve user home directory used by Rust STD -#pragma comment(lib, "userenv.lib") -// For base Windows API functions like file reads and writes -#pragma comment(lib, "ntdll.lib") -// For crypto functions called by LibAFL's random utilities -#pragma comment(lib, "bcrypt.lib") -// Required by windows_core -#pragma comment(lib, "ole32.lib") -// For debug facilities used in debug builds -#pragma comment(lib, "dbghelp.lib") -// For terminal controls used by crossterm -#pragma comment(lib, "user32.lib") + // For Windows API functions used by MiMalloc + #pragma comment(lib, "advapi32.lib") + // For Windows networking functionality used by LibAFL + #pragma comment(lib, "ws2_32.lib") + // For Windows API functions to retrieve user home directory used by Rust STD + #pragma comment(lib, "userenv.lib") + // For base Windows API functions like file reads and writes + #pragma comment(lib, "ntdll.lib") + // For crypto functions called by LibAFL's random utilities + #pragma comment(lib, "bcrypt.lib") + // Required by windows_core + #pragma comment(lib, "ole32.lib") + // For debug facilities used in debug builds + #pragma comment(lib, "dbghelp.lib") + // For terminal controls used by crossterm + #pragma comment(lib, "user32.lib") -#pragma comment(linker, "/export:LLVMFuzzerRunDriver") -#pragma comment(linker, "/export:__sanitizer_cov_8bit_counters_init") -#endif // _WIN32 + #pragma comment(linker, "/export:LLVMFuzzerRunDriver") + #pragma comment(linker, "/export:__sanitizer_cov_8bit_counters_init") +#endif // _WIN32 From 34a41f893932a01796d8ee9a72ad5c0974212f07 Mon Sep 17 00:00:00 2001 From: Rowan Hart Date: Mon, 7 Apr 2025 10:59:05 -0700 Subject: [PATCH 06/22] Clippy happiness pass --- libafl_libfuzzer/runtime/Cargo.toml.template | 4 +- libafl_libfuzzer/runtime/src/fuzz.rs | 145 ++++++++---------- libafl_libfuzzer/runtime/src/harness_wrap.cpp | 2 - libafl_libfuzzer/runtime/src/lib.rs | 16 +- libafl_libfuzzer/runtime/src/merge.rs | 7 +- libafl_libfuzzer/runtime/src/options.rs | 4 + libafl_libfuzzer/runtime/src/tmin.rs | 25 +-- 7 files changed, 99 insertions(+), 104 deletions(-) diff --git a/libafl_libfuzzer/runtime/Cargo.toml.template b/libafl_libfuzzer/runtime/Cargo.toml.template index 58f8d2fa0df..81621d35859 100644 --- a/libafl_libfuzzer/runtime/Cargo.toml.template +++ b/libafl_libfuzzer/runtime/Cargo.toml.template @@ -5,7 +5,7 @@ edition = "2024" publish = false [features] -default = ["tui_monitor"] +default = [] ## Enables forking mode for the LibAFL launcher (instead of starting new processes) fork = ["libafl/fork"] track_hit_feedbacks = [ @@ -16,7 +16,7 @@ tui_monitor = ["libafl/tui_monitor"] [target.'cfg(not(windows))'.features] ## Enable the `fork` feature on non-windows platforms -default = ["fork"] +default = ["fork", "tui_monitor"] [profile.release] lto = true diff --git a/libafl_libfuzzer/runtime/src/fuzz.rs b/libafl_libfuzzer/runtime/src/fuzz.rs index ffea7e4a2b9..ff08b8ced6e 100644 --- a/libafl_libfuzzer/runtime/src/fuzz.rs +++ b/libafl_libfuzzer/runtime/src/fuzz.rs @@ -1,22 +1,31 @@ use core::ffi::c_int; #[cfg(unix)] -use std::io::{Write, os::fd::AsRawFd, stderr, stdout}; -use std::{fmt::Debug, fs::File, net::TcpListener, str::FromStr}; +use std::{ + fmt::Debug, + fs::File, + io::{Write, stderr, stdout}, + net::TcpListener, + os::fd::AsRawFd, + str::FromStr, +}; +#[cfg(unix)] +use libafl::events::{ + EventConfig, SimpleRestartingEventManager, launcher::Launcher, monitors::Monitor, +}; #[cfg(feature = "tui_monitor")] use libafl::monitors::tui::TuiMonitor; use libafl::{ Error, Fuzzer, HasMetadata, corpus::Corpus, - events::{ - EventConfig, EventReceiver, ProgressReporter, SimpleEventManager, - SimpleRestartingEventManager, launcher::Launcher, - }, + events::{EventReceiver, ProgressReporter, SimpleEventManager}, executors::ExitKind, - monitors::{Monitor, MultiMonitor}, + monitors::MultiMonitor, stages::StagesTuple, state::{HasCurrentStageId, HasExecutions, HasLastReportTime, HasSolutions, Stoppable}, }; + +#[cfg(unix)] use libafl_bolts::{ core_affinity::Cores, shmem::{ShMemProvider, StdShMemProvider}, @@ -24,29 +33,27 @@ use libafl_bolts::{ use crate::{feedbacks::LibfuzzerCrashCauseMetadata, fuzz_with, options::LibfuzzerOptions}; +#[cfg(unix)] fn destroy_output_fds(options: &LibfuzzerOptions) { - #[cfg(unix)] - { - use libafl_bolts::os::{dup2, null_fd}; + use libafl_bolts::os::{dup2, null_fd}; - let null_fd = null_fd().unwrap(); - let stdout_fd = stdout().as_raw_fd(); - let stderr_fd = stderr().as_raw_fd(); + let null_fd = null_fd().unwrap(); + let stdout_fd = stdout().as_raw_fd(); + let stderr_fd = stderr().as_raw_fd(); - #[cfg(feature = "tui_monitor")] - if options.tui() { + #[cfg(feature = "tui_monitor")] + if options.tui() { + dup2(null_fd, stdout_fd).unwrap(); + dup2(null_fd, stderr_fd).unwrap(); + return; + } + + if options.close_fd_mask() != 0 { + if options.close_fd_mask() & u8::try_from(stderr_fd).unwrap() != 0 { dup2(null_fd, stdout_fd).unwrap(); - dup2(null_fd, stderr_fd).unwrap(); - return; } - - if options.close_fd_mask() != 0 { - if options.close_fd_mask() & u8::try_from(stderr_fd).unwrap() != 0 { - dup2(null_fd, stdout_fd).unwrap(); - } - if options.close_fd_mask() & u8::try_from(stderr_fd).unwrap() != 0 { - dup2(null_fd, stderr_fd).unwrap(); - } + if options.close_fd_mask() & u8::try_from(stderr_fd).unwrap() != 0 { + dup2(null_fd, stderr_fd).unwrap(); } } } @@ -128,22 +135,6 @@ where }) } -#[cfg(windows)] -fn fuzz_single_forking( - options: &LibfuzzerOptions, - harness: &extern "C" fn(*const u8, usize) -> c_int, - shmem_provider: StdShMemProvider, - monitor: M, -) -> Result<(), Error> -where - M: Monitor + Debug, -{ - panic!("Forking not supported on Windows"); -} - -/// Communicate the selected port to subprocesses -const PORT_PROVIDER_VAR: &str = "_LIBAFL_LIBFUZZER_FORK_PORT"; - #[cfg(unix)] fn fuzz_many_forking( options: &LibfuzzerOptions, @@ -155,6 +146,9 @@ fn fuzz_many_forking( where M: Monitor + Clone + Debug + 'static, { + // Communicate the selected port to subprocesses + const PORT_PROVIDER_VAR: &str = "_LIBAFL_LIBFUZZER_FORK_PORT"; + destroy_output_fds(options); let broker_port = std::env::var(PORT_PROVIDER_VAR) .map_err(Error::from) @@ -191,20 +185,6 @@ where }) } -#[cfg(windows)] -fn fuzz_many_forking( - options: &LibfuzzerOptions, - harness: &extern "C" fn(*const u8, usize) -> c_int, - shmem_provider: StdShMemProvider, - forks: usize, - monitor: M, -) -> Result<(), Error> -where - M: Monitor + Clone + Debug + 'static, -{ - panic!("Forking not supported on Windows"); -} - fn create_monitor_closure() -> impl Fn(&str) + Clone { #[cfg(unix)] let stderr_fd = @@ -229,18 +209,17 @@ pub fn fuzz( options: &LibfuzzerOptions, harness: &extern "C" fn(*const u8, usize) -> c_int, ) -> Result<(), Error> { + #[cfg(unix)] if let Some(forks) = options.forks() { let shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory"); #[cfg(feature = "tui_monitor")] - { - if options.tui() { - let monitor = TuiMonitor::builder() - .title(options.fuzzer_name()) - .enhanced_graphics(true) - .build(); - return fuzz_many_forking(options, harness, shmem_provider, forks, monitor); - } + if options.tui() { + let monitor = TuiMonitor::builder() + .title(options.fuzzer_name()) + .enhanced_graphics(true) + .build(); + return fuzz_many_forking(options, harness, shmem_provider, forks, monitor); } // Non-TUI path or when tui_monitor feature is disabled @@ -250,26 +229,26 @@ pub fn fuzz( } else { fuzz_many_forking(options, harness, shmem_provider, forks, monitor) } - } else { - #[cfg(feature = "tui_monitor")] - { - if options.tui() { - // if the user specifies TUI, we assume they want to fork; it would not be possible to use - // TUI safely otherwise - let shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory"); - let monitor = TuiMonitor::builder() - .title(options.fuzzer_name()) - .enhanced_graphics(true) - .build(); - return fuzz_many_forking(options, harness, shmem_provider, 1, monitor); - } - } + } - // Default path when no forks or TUI are specified, or when tui_monitor feature is disabled - destroy_output_fds(options); - fuzz_with!(options, harness, do_fuzz, |fuzz_single| { - let mgr = SimpleEventManager::new(MultiMonitor::new(create_monitor_closure())); - crate::start_fuzzing_single(fuzz_single, None, mgr) - }) + #[cfg(feature = "tui_monitor")] + if options.tui() { + // if the user specifies TUI, we assume they want to fork; it would not be possible to use + // TUI safely otherwise + let shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory"); + let monitor = TuiMonitor::builder() + .title(options.fuzzer_name()) + .enhanced_graphics(true) + .build(); + return fuzz_many_forking(options, harness, shmem_provider, 1, monitor); } + + // Default path when no forks or TUI are specified, or when tui_monitor feature is disabled + #[cfg(unix)] + destroy_output_fds(options); + + fuzz_with!(options, harness, do_fuzz, |fuzz_single| { + let mgr = SimpleEventManager::new(MultiMonitor::new(create_monitor_closure())); + crate::start_fuzzing_single(fuzz_single, None, mgr) + }) } diff --git a/libafl_libfuzzer/runtime/src/harness_wrap.cpp b/libafl_libfuzzer/runtime/src/harness_wrap.cpp index 1de2ac33dec..1d90255348f 100644 --- a/libafl_libfuzzer/runtime/src/harness_wrap.cpp +++ b/libafl_libfuzzer/runtime/src/harness_wrap.cpp @@ -24,8 +24,6 @@ extern "C" int libafl_libfuzzer_test_one_input( #pragma comment(lib, "ole32.lib") // For debug facilities used in debug builds #pragma comment(lib, "dbghelp.lib") - // For terminal controls used by crossterm - #pragma comment(lib, "user32.lib") #pragma comment(linker, "/export:LLVMFuzzerRunDriver") #pragma comment(linker, "/export:__sanitizer_cov_8bit_counters_init") diff --git a/libafl_libfuzzer/runtime/src/lib.rs b/libafl_libfuzzer/runtime/src/lib.rs index ce50c02db71..e2411e2d58d 100644 --- a/libafl_libfuzzer/runtime/src/lib.rs +++ b/libafl_libfuzzer/runtime/src/lib.rs @@ -67,11 +67,14 @@ #![allow(clippy::borrow_deref_ref)] use core::ffi::{CStr, c_char, c_int}; -use std::{fs::File, io::stderr}; #[cfg(unix)] -use std::os::fd::RawFd; +use std::{ + os::fd::RawFd, + {fs::File, io::stderr}, +}; +#[cfg(unix)] use env_logger::Target; use libafl::{ Error, @@ -331,11 +334,20 @@ macro_rules! fuzz_with { // Attempt to use tokens from libfuzzer dicts if !state.has_metadata::() { + #[cfg(any(target_os = "linux", target_vendor = "apple"))] let mut toks = if let Some(tokens) = $options.dict() { tokens.clone() } else { Tokens::default() }; + + #[cfg(not(any(target_os = "linux", target_vendor = "apple")))] + let toks = if let Some(tokens) = $options.dict() { + tokens.clone() + } else { + Tokens::default() + }; + #[cfg(any(target_os = "linux", target_vendor = "apple"))] { toks += libafl_targets::autotokens()?; diff --git a/libafl_libfuzzer/runtime/src/merge.rs b/libafl_libfuzzer/runtime/src/merge.rs index 5b0f6f0ddd1..a042d4d6333 100644 --- a/libafl_libfuzzer/runtime/src/merge.rs +++ b/libafl_libfuzzer/runtime/src/merge.rs @@ -1,10 +1,9 @@ +use std::{env::temp_dir, ffi::c_int, fs::rename}; #[cfg(unix)] -use std::os::fd::{AsRawFd, FromRawFd}; use std::{ - env::temp_dir, - ffi::c_int, - fs::{File, rename}, + fs::File, io::Write, + os::fd::{AsRawFd, FromRawFd}, }; use libafl::{ diff --git a/libafl_libfuzzer/runtime/src/options.rs b/libafl_libfuzzer/runtime/src/options.rs index a6031338b66..164e299137f 100644 --- a/libafl_libfuzzer/runtime/src/options.rs +++ b/libafl_libfuzzer/runtime/src/options.rs @@ -102,6 +102,7 @@ impl Default for ArtifactPrefix { #[derive(Debug, Clone)] #[expect(clippy::struct_excessive_bools)] pub struct LibfuzzerOptions { + #[allow(unused)] fuzzer_name: String, mode: LibfuzzerMode, artifact_prefix: ArtifactPrefix, @@ -122,6 +123,7 @@ pub struct LibfuzzerOptions { skip_tracing: bool, tui: bool, runs: usize, + #[allow(unused)] close_fd_mask: u8, unknown: Vec, } @@ -144,6 +146,7 @@ impl LibfuzzerOptions { .map(|builder| builder.build(name)) } + #[cfg(unix)] pub fn fuzzer_name(&self) -> &str { &self.fuzzer_name } @@ -224,6 +227,7 @@ impl LibfuzzerOptions { self.runs } + #[cfg(unix)] pub fn close_fd_mask(&self) -> u8 { self.close_fd_mask } diff --git a/libafl_libfuzzer/runtime/src/tmin.rs b/libafl_libfuzzer/runtime/src/tmin.rs index 9cee0b1b345..e77b6bb2d5a 100644 --- a/libafl_libfuzzer/runtime/src/tmin.rs +++ b/libafl_libfuzzer/runtime/src/tmin.rs @@ -19,10 +19,11 @@ use libafl::{ stages::StdTMinMutationalStage, state::{HasCorpus, StdState}, }; +#[cfg(unix)] +use libafl_bolts::shmem::{ShMemProvider, StdShMemProvider}; use libafl_bolts::{ AsSlice, HasLen, rands::{RomuDuoJrRand, StdRand}, - shmem::{ShMemProvider, StdShMemProvider}, tuples::tuple_list, }; use libafl_targets::LLVMCustomMutator; @@ -65,17 +66,19 @@ fn minimize_crash_with_mutator>( let mut fuzzer = StdFuzzer::new(QueueScheduler::new(), (), ()); - let shmem_provider = StdShMemProvider::new()?; #[cfg(unix)] - let mut executor = InProcessForkExecutor::new( - &mut harness, - (), - &mut fuzzer, - &mut state, - &mut mgr, - options.timeout(), - shmem_provider, - )?; + { + let shmem_provider = StdShMemProvider::new()?; + let mut executor = InProcessForkExecutor::new( + &mut harness, + (), + &mut fuzzer, + &mut state, + &mut mgr, + options.timeout(), + shmem_provider, + )?; + } #[cfg(windows)] let mut executor = InProcessExecutor::with_timeout( From 68b6478142903454d44b75f708f645b972254217 Mon Sep 17 00:00:00 2001 From: Rowan Hart Date: Mon, 7 Apr 2025 11:11:29 -0700 Subject: [PATCH 07/22] Clippy happiness pass --- libafl_libfuzzer/runtime/src/fuzz.rs | 1 - libafl_libfuzzer/runtime/src/lib.rs | 1 - libafl_libfuzzer/runtime/src/tmin.rs | 8 ++++---- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/libafl_libfuzzer/runtime/src/fuzz.rs b/libafl_libfuzzer/runtime/src/fuzz.rs index ff08b8ced6e..0fdd301621a 100644 --- a/libafl_libfuzzer/runtime/src/fuzz.rs +++ b/libafl_libfuzzer/runtime/src/fuzz.rs @@ -24,7 +24,6 @@ use libafl::{ stages::StagesTuple, state::{HasCurrentStageId, HasExecutions, HasLastReportTime, HasSolutions, Stoppable}, }; - #[cfg(unix)] use libafl_bolts::{ core_affinity::Cores, diff --git a/libafl_libfuzzer/runtime/src/lib.rs b/libafl_libfuzzer/runtime/src/lib.rs index e2411e2d58d..f48da6130e8 100644 --- a/libafl_libfuzzer/runtime/src/lib.rs +++ b/libafl_libfuzzer/runtime/src/lib.rs @@ -67,7 +67,6 @@ #![allow(clippy::borrow_deref_ref)] use core::ffi::{CStr, c_char, c_int}; - #[cfg(unix)] use std::{ os::fd::RawFd, diff --git a/libafl_libfuzzer/runtime/src/tmin.rs b/libafl_libfuzzer/runtime/src/tmin.rs index e77b6bb2d5a..ccf5a904552 100644 --- a/libafl_libfuzzer/runtime/src/tmin.rs +++ b/libafl_libfuzzer/runtime/src/tmin.rs @@ -67,9 +67,9 @@ fn minimize_crash_with_mutator>( let mut fuzzer = StdFuzzer::new(QueueScheduler::new(), (), ()); #[cfg(unix)] - { + let mut executor = { let shmem_provider = StdShMemProvider::new()?; - let mut executor = InProcessForkExecutor::new( + InProcessForkExecutor::new( &mut harness, (), &mut fuzzer, @@ -77,8 +77,8 @@ fn minimize_crash_with_mutator>( &mut mgr, options.timeout(), shmem_provider, - )?; - } + )? + }; #[cfg(windows)] let mut executor = InProcessExecutor::with_timeout( From 17399feff496861945a4b340c0b464a61fe555b7 Mon Sep 17 00:00:00 2001 From: Rowan Hart Date: Mon, 7 Apr 2025 11:12:31 -0700 Subject: [PATCH 08/22] Clippy happiness pass --- libafl_libfuzzer/runtime/src/fuzz.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/libafl_libfuzzer/runtime/src/fuzz.rs b/libafl_libfuzzer/runtime/src/fuzz.rs index 0fdd301621a..1ceaeb39228 100644 --- a/libafl_libfuzzer/runtime/src/fuzz.rs +++ b/libafl_libfuzzer/runtime/src/fuzz.rs @@ -9,10 +9,6 @@ use std::{ str::FromStr, }; -#[cfg(unix)] -use libafl::events::{ - EventConfig, SimpleRestartingEventManager, launcher::Launcher, monitors::Monitor, -}; #[cfg(feature = "tui_monitor")] use libafl::monitors::tui::TuiMonitor; use libafl::{ @@ -25,6 +21,11 @@ use libafl::{ state::{HasCurrentStageId, HasExecutions, HasLastReportTime, HasSolutions, Stoppable}, }; #[cfg(unix)] +use libafl::{ + events::{EventConfig, SimpleRestartingEventManager, launcher::Launcher}, + monitors::Monitor, +}; +#[cfg(unix)] use libafl_bolts::{ core_affinity::Cores, shmem::{ShMemProvider, StdShMemProvider}, From c45a9dcd1e21312636849e10c116fb11e420d5ca Mon Sep 17 00:00:00 2001 From: Rowan Hart Date: Mon, 7 Apr 2025 11:26:18 -0700 Subject: [PATCH 09/22] Correct logic --- libafl_libfuzzer/runtime/src/fuzz.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libafl_libfuzzer/runtime/src/fuzz.rs b/libafl_libfuzzer/runtime/src/fuzz.rs index 1ceaeb39228..44c9bad6893 100644 --- a/libafl_libfuzzer/runtime/src/fuzz.rs +++ b/libafl_libfuzzer/runtime/src/fuzz.rs @@ -225,9 +225,9 @@ pub fn fuzz( // Non-TUI path or when tui_monitor feature is disabled let monitor = MultiMonitor::new(create_monitor_closure()); if forks == 1 { - fuzz_single_forking(options, harness, shmem_provider, monitor) + return fuzz_single_forking(options, harness, shmem_provider, monitor); } else { - fuzz_many_forking(options, harness, shmem_provider, forks, monitor) + return fuzz_many_forking(options, harness, shmem_provider, forks, monitor); } } From cb2afd1944d0b5aa6b4678f71e588e00d6d0b8b4 Mon Sep 17 00:00:00 2001 From: Rowan Hart Date: Mon, 7 Apr 2025 11:54:29 -0700 Subject: [PATCH 10/22] Correct logic --- libafl_libfuzzer/runtime/src/fuzz.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libafl_libfuzzer/runtime/src/fuzz.rs b/libafl_libfuzzer/runtime/src/fuzz.rs index 44c9bad6893..0110179fcdd 100644 --- a/libafl_libfuzzer/runtime/src/fuzz.rs +++ b/libafl_libfuzzer/runtime/src/fuzz.rs @@ -224,11 +224,12 @@ pub fn fuzz( // Non-TUI path or when tui_monitor feature is disabled let monitor = MultiMonitor::new(create_monitor_closure()); + if forks == 1 { return fuzz_single_forking(options, harness, shmem_provider, monitor); - } else { - return fuzz_many_forking(options, harness, shmem_provider, forks, monitor); } + + return fuzz_many_forking(options, harness, shmem_provider, forks, monitor); } #[cfg(feature = "tui_monitor")] From ff613eb9a8758a9432fbbd9e4ac0f818d57c4910 Mon Sep 17 00:00:00 2001 From: Rowan Hart Date: Tue, 20 May 2025 11:58:21 -0700 Subject: [PATCH 11/22] Update help output and make runs argument work --- libafl_libfuzzer/runtime/src/fuzz.rs | 8 +- libafl_libfuzzer/runtime/src/options.rs | 122 +++++++----------------- 2 files changed, 43 insertions(+), 87 deletions(-) diff --git a/libafl_libfuzzer/runtime/src/fuzz.rs b/libafl_libfuzzer/runtime/src/fuzz.rs index 0110179fcdd..e6f3147d8c7 100644 --- a/libafl_libfuzzer/runtime/src/fuzz.rs +++ b/libafl_libfuzzer/runtime/src/fuzz.rs @@ -100,7 +100,13 @@ where return Err(Error::shutting_down()); } } - fuzzer.fuzz_loop(stages, executor, state, mgr)?; + if options.runs() == 0 { + fuzzer.fuzz_loop(stages, executor, state, mgr)?; + } else { + for _ in 0..options.runs() { + fuzzer.fuzz_one(stages, executor, state, mgr)?; + } + } Ok(()) } diff --git a/libafl_libfuzzer/runtime/src/options.rs b/libafl_libfuzzer/runtime/src/options.rs index 164e299137f..6fe7496c0eb 100644 --- a/libafl_libfuzzer/runtime/src/options.rs +++ b/libafl_libfuzzer/runtime/src/options.rs @@ -364,93 +364,43 @@ impl<'a> LibfuzzerOptionsBuilder<'a> { "runs" => self.runs = parse_or_bail!(name, value, usize), "close_fd_mask" => self.close_fd_mask = parse_or_bail!(name, value, u8), "help" => { - println!("Usage:"); - println!(); - println!("To run fuzzing pass 0 or more directories."); - println!("{name} [-flag1=val1 [-flag2=val2 ...] ] [dir1 [dir2 ...] ]"); - println!(); println!( - "To run individual tests without fuzzing pass 1 or more files:" - ); - println!("{name} [-flag1=val1 [-flag2=val2 ...] ] file1 [file2 ...]"); - println!(); - println!("Flags: (strictly in form -flag=value)"); - println!( - " artifact_prefix 0 Write fuzzing artifacts (crash, timeout, or slow inputs) as $(artifact_prefix)file" - ); - println!( - " timeout 1200 Timeout in seconds. If one unit runs more than this number of seconds the process will abort." - ); - println!( - " grimoire 0 If 1, enable the Grimoire mutator that is structure-aware." - ); - println!( - " use_value_profile 0 Use value profile to guide fuzzing." - ); - println!( - " unicode 1 If 1, generate Unicode inputs." - ); - println!( - " dict 0 Use the dictionary file." - ); - #[cfg(not(windows))] - println!( - " fork 0 Number of forks to use (>1 requires Unix-like OS)." - ); - println!( - " jobs 0 Same as fork. Number of jobs to run with stdout/stderr redirected." - ); - println!( - " ignore_crashes 0 If 1, ignore crashes in fork mode." - ); - println!( - " ignore_timeouts 0 If 1, ignore timeouts in fork mode." - ); - println!( - " ignore_ooms 0 If 1, ignore out-of-memory errors in fork mode." - ); - println!( - " rss_limit_mb 2048 If non-zero, the fuzzer will exit upon reaching this limit of RSS memory usage (in Mb)." - ); - println!( - " malloc_limit_mb 2048 If non-zero, the fuzzer will exit if the target tries to allocate this number of Mb with one malloc call." - ); - println!( - " ignore_remaining_args 0 If 1, ignore all arguments passed after this one." - ); - println!( - " dedup 0 If 1, deduplicate corpus elements." - ); - println!( - " shrink 0 If 1, try to shrink corpus elements." - ); - println!( - " skip_tracing 0 If 1, skip coverage tracing for faster execution." - ); - println!( - " tui 0 If 1, use the terminal UI interface." - ); - println!( - " runs 0 Number of individual test runs (0 for infinite runs)." - ); - println!( - " close_fd_mask 0 If 1, close stdout; if 2, close stderr; if 3, close both." - ); - println!( - " merge 0 If 1, merge multiple corpora into a single one." - ); - println!( - " minimize_crash 0 If 1, minimize crashes to their smallest reproducing input." - ); - println!( - " report 0 If 1, report statistics without actually fuzzing." - ); - println!( - " help 0 Print this help message." - ); - println!(); - println!( - "Flags starting with '--' will be ignored and will be passed verbatim to subprocesses." + r#"Usage: + + To run fuzzing pass 0 or more directories. + {name} [-flag1=val1 [-flag2=val2 ...] ] [dir1 [dir2 ...] ] + + To run individual tests without fuzzing pass 1 or more files: + {name} [-flag1=val1 [-flag2=val2 ...] ] file1 [file2 ...] + + Flags: (strictly in form -flag=value)"); + artifact_prefix 0 Write fuzzing artifacts (crash, timeout, or slow inputs) as $(artifact_prefix)file + timeout 1200 Timeout in seconds. If one unit runs more than this number of seconds the process will abort. + grimoire 0 If 1, enable the Grimoire mutator that is structure-aware. + use_value_profile 0 Use value profile to guide fuzzing. + unicode 1 If 1, generate Unicode inputs. + dict 0 Use the dictionary file. + fork 0 Number of forks to use (>1 requires Unix-like OS). + jobs 0 Same as fork. Number of jobs to run with stdout/stderr redirected. + ignore_crashes 0 If 1, ignore crashes in fork mode. + ignore_timeouts 0 If 1, ignore timeouts in fork mode. + ignore_ooms 0 If 1, ignore out-of-memory errors in fork mode. + rss_limit_mb 2048 If non-zero, the fuzzer will exit upon reaching this limit of RSS memory usage (in Mb). + malloc_limit_mb 2048 If non-zero, the fuzzer will exit if the target tries to allocate this number of Mb with one malloc call. + ignore_remaining_args 0 If 1, ignore all arguments passed after this one. + dedup 0 If 1, deduplicate corpus elements. + shrink 0 If 1, try to shrink corpus elements. + skip_tracing 0 If 1, skip coverage tracing for faster execution. + tui 0 If 1, use the terminal UI interface. + runs 0 Number of individual test runs (0 for infinite runs). + close_fd_mask 0 If 1, close stdout; if 2, close stderr; if 3, close both. + merge 0 If 1, merge multiple corpora into a single one. + minimize_crash 0 If 1, minimize crashes to their smallest reproducing input. + report 0 If 1, report statistics without actually fuzzing. + help 0 Print this help message. + + Flags starting with '--' will be ignored and will be passed verbatim to subprocesses. + "# ); std::process::exit(0); } From 8c6210dc0d528e681b82ea44baad7a4f916600a0 Mon Sep 17 00:00:00 2001 From: Rowan Hart Date: Tue, 20 May 2025 11:59:07 -0700 Subject: [PATCH 12/22] Add test for libafl_libfuzzer on windows --- .../libafl_libfuzzer_windows/Justfile | 30 +++++++++++++++++ .../libafl_libfuzzer_windows/README.md | 4 +++ .../libafl_libfuzzer_windows/harness.cc | 33 +++++++++++++++++++ 3 files changed, 67 insertions(+) create mode 100644 fuzzers/inprocess/libafl_libfuzzer_windows/Justfile create mode 100644 fuzzers/inprocess/libafl_libfuzzer_windows/README.md create mode 100644 fuzzers/inprocess/libafl_libfuzzer_windows/harness.cc diff --git a/fuzzers/inprocess/libafl_libfuzzer_windows/Justfile b/fuzzers/inprocess/libafl_libfuzzer_windows/Justfile new file mode 100644 index 00000000000..93c0485bf75 --- /dev/null +++ b/fuzzers/inprocess/libafl_libfuzzer_windows/Justfile @@ -0,0 +1,30 @@ +import "../../../just/libafl.just" + +FUZZER_NAME := "libafl_libfuzzer_windows" +FUZZER_NAME_WIN := "libafl_libfuzzer_windows.exe" + +set windows-shell := ['cmd.exe', '/c'] +set unstable + +[windows] +libafl_libfuzzer: + powershell -File ..\..\..\libafl_libfuzzer_runtime\build.ps1 + +[windows] +harness: libafl_libfuzzer + copy ..\..\..\libafl_libfuzzer_runtime\target\release\afl_libfuzzer_runtime.lib . + cl /c /O2 /EHsc /std:c++17 /MDd /fsanitize-coverage=inline-8bit-counters /fsanitize-coverage=edge /fsanitize-coverage=trace-cmp /fsanitize-coverage=trace-div /Fo:harness.obj harness.cc + link harness.obj afl_libfuzzer_runtime.lib sancov.lib /OUT:libafl_libfuzzer_windows.exe + +[windows] +run: harness + if not exist corpus mkdir corpus + {{FUZZER_NAME_WIN}} -use_value_profile=1 corpus + +[windows] +[script("cmd.exe", "/c")] +test: harness + if exist corpus rd /s /q corpus + mkdir corpus + {{FUZZER_NAME_WIN}} -use_value_profile=1 -runs=30000 corpus + dir /a-d corpus && (echo Files exist) || (exit /b 1337) \ No newline at end of file diff --git a/fuzzers/inprocess/libafl_libfuzzer_windows/README.md b/fuzzers/inprocess/libafl_libfuzzer_windows/README.md new file mode 100644 index 00000000000..114eb10444a --- /dev/null +++ b/fuzzers/inprocess/libafl_libfuzzer_windows/README.md @@ -0,0 +1,4 @@ +# LibAFL-LibFuzzer Windows + +A simple example demonstrating how to build LibFuzzer harnesses with LibAFL-LibFuzzer +as an alternative runtime on Windows. \ No newline at end of file diff --git a/fuzzers/inprocess/libafl_libfuzzer_windows/harness.cc b/fuzzers/inprocess/libafl_libfuzzer_windows/harness.cc new file mode 100644 index 00000000000..267312ad8a8 --- /dev/null +++ b/fuzzers/inprocess/libafl_libfuzzer_windows/harness.cc @@ -0,0 +1,33 @@ +// Simple decoder function with an off by one error that is triggered under certain +// conditions. + +#include +#include + +int DecodeInput(const uint8_t *data, size_t size) { + if (size < 5) { + return -1; // Error: not enough data + } + + if (data[0] != 'F' || data[1] != 'U' || data[2] != 'Z' || data[3] == 'Z') { + return -1; // Error: invalid header + } + + if (data[4] <= 0) { + return -1; // Error: invalid size + } + + int csum = 0; + + for (size_t i = 5; i < size; ++i) { + csum += data[i]; + } + + return csum; // Error: checksum mismatch +} + + +extern "C" __declspec(dllexport) int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + DecodeInput(data, size); + return 0; +} \ No newline at end of file From f4c129155a2caf67350e459b6ec91f1e20035cd7 Mon Sep 17 00:00:00 2001 From: Rowan Hart Date: Tue, 20 May 2025 12:01:58 -0700 Subject: [PATCH 13/22] Add workflow for libafl_libfuzzer test --- .github/workflows/build_and_test.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 51d5d11d24e..aa6f1bdd664 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -522,6 +522,16 @@ jobs: - name: Build fuzzers/binary_only/frida_libpng run: cd fuzzers/binary_only/frida_libpng/ && just test + windows-libafl-libfuzzer: + runs-on: windows-latest + needs: + - common + steps: + - uses: actions/checkout@v4 + - uses: ./.github/workflows/windows-tester-prepare + - name: Build fuzzers/binary_only/frida_libpng + run: cd fuzzers/inprocess/libafl_libfuzzer_windows && just test + windows-libfuzzer-stb-image: runs-on: windows-latest needs: From 6558263ae583b588be2e63568390c74df8a3ebfa Mon Sep 17 00:00:00 2001 From: Rowan Hart Date: Tue, 20 May 2025 12:02:13 -0700 Subject: [PATCH 14/22] Fix copy without dependent task --- fuzzers/binary_only/frida_libpng/Justfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fuzzers/binary_only/frida_libpng/Justfile b/fuzzers/binary_only/frida_libpng/Justfile index 2b607f8ea50..98f1045953a 100644 --- a/fuzzers/binary_only/frida_libpng/Justfile +++ b/fuzzers/binary_only/frida_libpng/Justfile @@ -47,13 +47,14 @@ lib2: libpng [windows] harness: lib lib2 - copy libpng-1.6.37\Release\libpng16.lib . && copy libpng-1.6.37\Release\libpng16.dll . && copy zlib\Release\zlib.lib . && copy zlib\Release\zlib.dll . && copy target\release\frida_fuzzer.exe . + copy libpng-1.6.37\Release\libpng16.lib . && copy libpng-1.6.37\Release\libpng16.dll . && copy zlib\Release\zlib.lib . && copy zlib\Release\zlib.dll . cl /O2 /c /I .\libpng-1.6.37 harness.cc /Fo:harness.obj && link /DLL /OUT:libpng-harness.dll harness.obj libpng16.lib zlib.lib [unix] [windows] build: cargo build --profile {{ PROFILE }} + copy target\release\frida_fuzzer.exe . [unix] run: build harness From a17baa4cf53f671151f561041bdb6ed0cd9c462a Mon Sep 17 00:00:00 2001 From: Rowan Hart Date: Tue, 20 May 2025 12:04:17 -0700 Subject: [PATCH 15/22] Add libafl_libfuzzer_windows to preflight list --- .github/workflows/build_and_test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index aa6f1bdd664..77f55114ca7 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -316,6 +316,7 @@ jobs: - ./fuzzers/inprocess/libfuzzer_stb_image # - ./fuzzers/structure_aware/libfuzzer_stb_image_concolic # - ./fuzzers/inprocess/sqlite_centralized_multi_machine + - ./fuzzers/inprocess/libafl_libfuzzer_windows # Fuzz Anything - ./fuzzers/fuzz_anything/push_harness From bc2f70bd4eeca472c326306f645837dd289259ea Mon Sep 17 00:00:00 2001 From: Rowan Hart Date: Tue, 20 May 2025 12:05:09 -0700 Subject: [PATCH 16/22] Format harness --- .../libafl_libfuzzer_windows/harness.cc | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/fuzzers/inprocess/libafl_libfuzzer_windows/harness.cc b/fuzzers/inprocess/libafl_libfuzzer_windows/harness.cc index 267312ad8a8..256e92141fc 100644 --- a/fuzzers/inprocess/libafl_libfuzzer_windows/harness.cc +++ b/fuzzers/inprocess/libafl_libfuzzer_windows/harness.cc @@ -1,33 +1,33 @@ -// Simple decoder function with an off by one error that is triggered under certain -// conditions. +// Simple decoder function with an off by one error that is triggered under +// certain conditions. #include #include int DecodeInput(const uint8_t *data, size_t size) { - if (size < 5) { - return -1; // Error: not enough data - } + if (size < 5) { + return -1; // Error: not enough data + } - if (data[0] != 'F' || data[1] != 'U' || data[2] != 'Z' || data[3] == 'Z') { - return -1; // Error: invalid header - } + if (data[0] != 'F' || data[1] != 'U' || data[2] != 'Z' || data[3] == 'Z') { + return -1; // Error: invalid header + } - if (data[4] <= 0) { - return -1; // Error: invalid size - } + if (data[4] <= 0) { + return -1; // Error: invalid size + } - int csum = 0; + int csum = 0; - for (size_t i = 5; i < size; ++i) { - csum += data[i]; - } + for (size_t i = 5; i < size; ++i) { + csum += data[i]; + } - return csum; // Error: checksum mismatch + return csum; // Error: checksum mismatch } - -extern "C" __declspec(dllexport) int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { - DecodeInput(data, size); - return 0; +extern "C" __declspec(dllexport) int LLVMFuzzerTestOneInput(const uint8_t *data, + size_t size) { + DecodeInput(data, size); + return 0; } \ No newline at end of file From 599dea08e927f6bda18c9ee384df8d39d2d25491 Mon Sep 17 00:00:00 2001 From: Rowan Hart Date: Tue, 20 May 2025 12:07:08 -0700 Subject: [PATCH 17/22] Explicitly ignore windows fuzzer --- .github/workflows/build_and_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 77f55114ca7..68347186e2b 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -316,7 +316,7 @@ jobs: - ./fuzzers/inprocess/libfuzzer_stb_image # - ./fuzzers/structure_aware/libfuzzer_stb_image_concolic # - ./fuzzers/inprocess/sqlite_centralized_multi_machine - - ./fuzzers/inprocess/libafl_libfuzzer_windows + # - ./fuzzers/inprocess/libafl_libfuzzer_windows # Fuzz Anything - ./fuzzers/fuzz_anything/push_harness From 5dc689e4df3da1142028b8106d826b7e88c5bc8b Mon Sep 17 00:00:00 2001 From: Rowan Hart Date: Tue, 20 May 2025 12:45:02 -0700 Subject: [PATCH 18/22] Remove windows-specific copy from unix instructions --- fuzzers/binary_only/frida_libpng/Justfile | 1 - 1 file changed, 1 deletion(-) diff --git a/fuzzers/binary_only/frida_libpng/Justfile b/fuzzers/binary_only/frida_libpng/Justfile index 98f1045953a..8835f8cc891 100644 --- a/fuzzers/binary_only/frida_libpng/Justfile +++ b/fuzzers/binary_only/frida_libpng/Justfile @@ -54,7 +54,6 @@ harness: lib lib2 [windows] build: cargo build --profile {{ PROFILE }} - copy target\release\frida_fuzzer.exe . [unix] run: build harness From 5dd2bfd70d490cb47248019bc201f87041ae55ec Mon Sep 17 00:00:00 2001 From: Rowan Hart Date: Tue, 20 May 2025 12:49:31 -0700 Subject: [PATCH 19/22] Ensure using nightly --- .github/workflows/build_and_test.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 68347186e2b..133b8749765 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -530,6 +530,10 @@ jobs: steps: - uses: actions/checkout@v4 - uses: ./.github/workflows/windows-tester-prepare + # Ensure we're using nightly, because libafl-libfuzzer requries it + - uses: dtolnay/rust-toolchain@nightly + with: + components: llvm-tools, clippy, rustfmt - name: Build fuzzers/binary_only/frida_libpng run: cd fuzzers/inprocess/libafl_libfuzzer_windows && just test From a7d70d2fce37dbef122fb7e411edef66013e83d8 Mon Sep 17 00:00:00 2001 From: Rowan Hart Date: Tue, 20 May 2025 13:08:05 -0700 Subject: [PATCH 20/22] Fix job name --- .github/workflows/build_and_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 133b8749765..4f1481dfab1 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -534,7 +534,7 @@ jobs: - uses: dtolnay/rust-toolchain@nightly with: components: llvm-tools, clippy, rustfmt - - name: Build fuzzers/binary_only/frida_libpng + - name: Build fuzzers/inprocess/libafl_libfuzzer_windows run: cd fuzzers/inprocess/libafl_libfuzzer_windows && just test windows-libfuzzer-stb-image: From 6d47b67a06225b612701d10e0b3f6f6007e0afda Mon Sep 17 00:00:00 2001 From: Rowan Hart Date: Tue, 20 May 2025 14:55:25 -0700 Subject: [PATCH 21/22] Update build to use libFuzzer.lib on Windows to keep consistent with Linux --- .github/workflows/build_and_test.yml | 4 -- .../libafl_libfuzzer_windows/Justfile | 4 +- libafl_libfuzzer/README.md | 35 ++++++--- libafl_libfuzzer/runtime/src/options.rs | 72 +++++++++---------- libafl_libfuzzer_runtime/Justfile | 10 +++ libafl_libfuzzer_runtime/build.ps1 | 40 ++--------- 6 files changed, 76 insertions(+), 89 deletions(-) create mode 100644 libafl_libfuzzer_runtime/Justfile diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 4f1481dfab1..2fc176584ce 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -530,10 +530,6 @@ jobs: steps: - uses: actions/checkout@v4 - uses: ./.github/workflows/windows-tester-prepare - # Ensure we're using nightly, because libafl-libfuzzer requries it - - uses: dtolnay/rust-toolchain@nightly - with: - components: llvm-tools, clippy, rustfmt - name: Build fuzzers/inprocess/libafl_libfuzzer_windows run: cd fuzzers/inprocess/libafl_libfuzzer_windows && just test diff --git a/fuzzers/inprocess/libafl_libfuzzer_windows/Justfile b/fuzzers/inprocess/libafl_libfuzzer_windows/Justfile index 93c0485bf75..f69091ada14 100644 --- a/fuzzers/inprocess/libafl_libfuzzer_windows/Justfile +++ b/fuzzers/inprocess/libafl_libfuzzer_windows/Justfile @@ -12,9 +12,9 @@ libafl_libfuzzer: [windows] harness: libafl_libfuzzer - copy ..\..\..\libafl_libfuzzer_runtime\target\release\afl_libfuzzer_runtime.lib . + copy ..\..\..\libafl_libfuzzer_runtime\libFuzzer.lib . cl /c /O2 /EHsc /std:c++17 /MDd /fsanitize-coverage=inline-8bit-counters /fsanitize-coverage=edge /fsanitize-coverage=trace-cmp /fsanitize-coverage=trace-div /Fo:harness.obj harness.cc - link harness.obj afl_libfuzzer_runtime.lib sancov.lib /OUT:libafl_libfuzzer_windows.exe + link harness.obj libFuzzer.lib sancov.lib /OUT:libafl_libfuzzer_windows.exe [windows] run: harness diff --git a/libafl_libfuzzer/README.md b/libafl_libfuzzer/README.md index 05b1bf2f7c2..c6f58ce2acb 100644 --- a/libafl_libfuzzer/README.md +++ b/libafl_libfuzzer/README.md @@ -86,22 +86,35 @@ To do so, [ensure a recent nightly version of Rust is installed](https://rustup. [`libafl_libfuzzer_runtime`](../libafl_libfuzzer_runtime) folder and build the runtime with the following command: ```bash -./build.sh +just build ``` -The static library will be available at `libFuzzer.a` in the [`libafl_libfuzzer_runtime`](../libafl_libfuzzer_runtime) -directory. -If you encounter build failures without clear error outputs that help you resolve the issue, please [submit an issue]. +Or you can call `build.sh` (Unix) or `build.ps1` (Windows). -This library may now be used in place of libFuzzer. -To do so, change your CFLAGS/CXXFLAGS from `-fsanitize=fuzzer` to: +The static library will be available at `libFuzzer.a` (`libFuzzer.lib` for Windows) in +the [`libafl_libfuzzer_runtime`](../libafl_libfuzzer_runtime) directory. If you +encounter build failures without clear error outputs that help you resolve the issue, +please [submit an issue]. -``` --fsanitize=fuzzer-no-link -L/path/to/libafl_libfuzzer_runtime -lFuzzer -``` +#### Unix + +This library may now be used in place of libFuzzer. To do so, change your +CFLAGS/CXXFLAGS from `-fsanitize=fuzzer` to: + +``` -fsanitize=fuzzer-no-link -L/path/to/libafl_libfuzzer_runtime -lFuzzer ``` + +Alternatively, you may directly overwrite the system libFuzzer library and use +`-fsanitize=fuzzer` as normal. This changes per system, but on my machine is located at +`/usr/lib64/clang/16/lib/linux/libclang_rt.fuzzer-x86_64.a`. + +#### Windows + +For Windows, change your CFLAGS/CXXFLAGS from `-fsanitize=fuzzer` to: + +```/fsanitize-coverage=inline-8bit-counters /fsanitize-coverage=edge /fsanitize-coverage=trace-cmp /fsanitize-coverage=trace-div``` -Alternatively, you may directly overwrite the system libFuzzer library and use `-fsanitize=fuzzer` as normal. -This changes per system, but on my machine is located at `/usr/lib64/clang/16/lib/linux/libclang_rt.fuzzer-x86_64.a`. +And then ensure you link with `sancov.lib` when producing your final executable. See +`fuzzers\inprocess\libafl_libfuzzer_windows` for an example. #### Caveats diff --git a/libafl_libfuzzer/runtime/src/options.rs b/libafl_libfuzzer/runtime/src/options.rs index 6fe7496c0eb..ce903dcb101 100644 --- a/libafl_libfuzzer/runtime/src/options.rs +++ b/libafl_libfuzzer/runtime/src/options.rs @@ -365,42 +365,42 @@ impl<'a> LibfuzzerOptionsBuilder<'a> { "close_fd_mask" => self.close_fd_mask = parse_or_bail!(name, value, u8), "help" => { println!( - r#"Usage: - - To run fuzzing pass 0 or more directories. - {name} [-flag1=val1 [-flag2=val2 ...] ] [dir1 [dir2 ...] ] - - To run individual tests without fuzzing pass 1 or more files: - {name} [-flag1=val1 [-flag2=val2 ...] ] file1 [file2 ...] - - Flags: (strictly in form -flag=value)"); - artifact_prefix 0 Write fuzzing artifacts (crash, timeout, or slow inputs) as $(artifact_prefix)file - timeout 1200 Timeout in seconds. If one unit runs more than this number of seconds the process will abort. - grimoire 0 If 1, enable the Grimoire mutator that is structure-aware. - use_value_profile 0 Use value profile to guide fuzzing. - unicode 1 If 1, generate Unicode inputs. - dict 0 Use the dictionary file. - fork 0 Number of forks to use (>1 requires Unix-like OS). - jobs 0 Same as fork. Number of jobs to run with stdout/stderr redirected. - ignore_crashes 0 If 1, ignore crashes in fork mode. - ignore_timeouts 0 If 1, ignore timeouts in fork mode. - ignore_ooms 0 If 1, ignore out-of-memory errors in fork mode. - rss_limit_mb 2048 If non-zero, the fuzzer will exit upon reaching this limit of RSS memory usage (in Mb). - malloc_limit_mb 2048 If non-zero, the fuzzer will exit if the target tries to allocate this number of Mb with one malloc call. - ignore_remaining_args 0 If 1, ignore all arguments passed after this one. - dedup 0 If 1, deduplicate corpus elements. - shrink 0 If 1, try to shrink corpus elements. - skip_tracing 0 If 1, skip coverage tracing for faster execution. - tui 0 If 1, use the terminal UI interface. - runs 0 Number of individual test runs (0 for infinite runs). - close_fd_mask 0 If 1, close stdout; if 2, close stderr; if 3, close both. - merge 0 If 1, merge multiple corpora into a single one. - minimize_crash 0 If 1, minimize crashes to their smallest reproducing input. - report 0 If 1, report statistics without actually fuzzing. - help 0 Print this help message. - - Flags starting with '--' will be ignored and will be passed verbatim to subprocesses. - "# + "Usage:\n\ + \n\ + To run fuzzing pass 0 or more directories.\n\ + {name} [-flag1=val1 [-flag2=val2 ...] ] [dir1 [dir2 ...] ]\n\ + \n\ + To run individual tests without fuzzing pass 1 or more files:\n\ + {name} [-flag1=val1 [-flag2=val2 ...] ] file1 [file2 ...]\n\ + \n\ + Flags: (strictly in form -flag=value)\n\ + artifact_prefix 0 Write fuzzing artifacts (crash, timeout, or slow inputs) as $(artifact_prefix)file\n\ + timeout 1200 Timeout in seconds. If one unit runs more than this number of seconds the process will abort.\n\ + grimoire 0 If 1, enable the Grimoire mutator that is structure-aware.\n\ + use_value_profile 0 Use value profile to guide fuzzing.\n\ + unicode 1 If 1, generate Unicode inputs.\n\ + dict 0 Use the dictionary file.\n\ + fork 0 Number of forks to use (>1 requires Unix-like OS).\n\ + jobs 0 Same as fork. Number of jobs to run with stdout/stderr redirected.\n\ + ignore_crashes 0 If 1, ignore crashes in fork mode.\n\ + ignore_timeouts 0 If 1, ignore timeouts in fork mode.\n\ + ignore_ooms 0 If 1, ignore out-of-memory errors in fork mode.\n\ + rss_limit_mb 2048 If non-zero, the fuzzer will exit upon reaching this limit of RSS memory usage (in Mb).\n\ + malloc_limit_mb 2048 If non-zero, the fuzzer will exit if the target tries to allocate this number of Mb with one malloc call.\n\ + ignore_remaining_args 0 If 1, ignore all arguments passed after this one.\n\ + dedup 0 If 1, deduplicate corpus elements.\n\ + shrink 0 If 1, try to shrink corpus elements.\n\ + skip_tracing 0 If 1, skip coverage tracing for faster execution.\n\ + tui 0 If 1, use the terminal UI interface.\n\ + runs 0 Number of individual test runs (0 for infinite runs).\n\ + close_fd_mask 0 If 1, close stdout; if 2, close stderr; if 3, close both.\n\ + merge 0 If 1, merge multiple corpora into a single one.\n\ + minimize_crash 0 If 1, minimize crashes to their smallest reproducing input.\n\ + report 0 If 1, report statistics without actually fuzzing.\n\ + help 0 Print this help message.\n\ + \n\ + Flags starting with '--' will be ignored and will be passed verbatim to subprocesses.\n\ + " ); std::process::exit(0); } diff --git a/libafl_libfuzzer_runtime/Justfile b/libafl_libfuzzer_runtime/Justfile new file mode 100644 index 00000000000..ae991e7a5cb --- /dev/null +++ b/libafl_libfuzzer_runtime/Justfile @@ -0,0 +1,10 @@ +set windows-shell := ['cmd.exe', '/c'] +set unstable + +[unix] +build: + ./build.sh + +[windows] +build: + powershell -File build.ps1 \ No newline at end of file diff --git a/libafl_libfuzzer_runtime/build.ps1 b/libafl_libfuzzer_runtime/build.ps1 index 00bc2589791..a6d790a87c1 100644 --- a/libafl_libfuzzer_runtime/build.ps1 +++ b/libafl_libfuzzer_runtime/build.ps1 @@ -24,20 +24,7 @@ try { } Write-Host "Building libafl_libfuzzer runtime with profile '$profile'" -ForegroundColor Green -Invoke-Expression "cargo +nightly build --profile $profile" - -# target-libdir is e.g. C:\Users\user\.rustup\toolchain\nightly-x86_64-pc-windows-msvc\lib\rustlib\x86_64-pc-windows-msvc\lib -$RUSTC_BIN = Split-Path -Parent (Invoke-Expression "cargo +nightly rustc -Zunstable-options --print target-libdir") -$RUSTC_BIN = Join-Path $RUSTC_BIN "bin" -$RUST_LLD = Join-Path $RUSTC_BIN "rust-lld.exe" -$RUST_AR = Join-Path $RUSTC_BIN "llvm-ar.exe" -$RUST_NM = Join-Path $RUSTC_BIN "llvm-nm.exe" - -if (-not (Test-Path $RUST_LLD) -or -not (Test-Path $RUST_AR)) { - Write-Host "You must install the llvm-tools component: 'rustup component add llvm-tools'" -ForegroundColor Red - Write-Host "Could not find $RUST_LLD or $RUST_AR" -ForegroundColor Red - exit 1 -} +Invoke-Expression "cargo build --profile $profile" $tmpdir = Join-Path $env:TEMP ([System.IO.Path]::GetRandomFileName()) New-Item -ItemType Directory -Path $tmpdir | Out-Null @@ -58,32 +45,13 @@ try { $targetPath = Join-Path $SCRIPT_DIR "target\$profile\afl_libfuzzer_runtime.lib" $outputPath = Join-Path $SCRIPT_DIR "libFuzzer.lib" - Write-Host "Creating intermediate object file '$tmpdir\libFuzzer.obj from $targetPath'" -ForegroundColor Green - & $RUST_LLD -flavor link /lib /nologo /out:"$tmpdir\libFuzzer.obj" "$targetPath" - - if ($LASTEXITCODE -ne 0) { - throw "Failed to create intermediate object file" - } - - Write-Host "Creating final library '$outputPath'" -ForegroundColor Green - & $RUST_AR crs "$outputPath" "$tmpdir\libFuzzer.obj" + Copy-Item -Path $targetPath -Destination $outputPath -Force | Out-Null if ($LASTEXITCODE -ne 0) { - throw "Failed to create final library" - } - - Write-Host "Verifying symbols from '$outputPath'" -ForegroundColor Green - # Symbols that should be present: - # LLVMFuzzerRunDriver - & $RUST_NM "$outputPath" | Select-String "LLVMFuzzerRunDriver" | Out-Null - - if ($LASTEXITCODE -ne 0) { - throw "Failed to verify symbols in final library" + throw "Failed to copy final library" } Write-Host "Done! Wrote the runtime to '$outputPath'" -ForegroundColor Green - -} -finally { +} finally { Cleanup } \ No newline at end of file From 0b7c082692c81ed154ea3a9c58c3933f7ca793c9 Mon Sep 17 00:00:00 2001 From: Rowan Hart Date: Tue, 20 May 2025 15:05:57 -0700 Subject: [PATCH 22/22] Remove nightly requirement --- libafl_libfuzzer_runtime/build.ps1 | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/libafl_libfuzzer_runtime/build.ps1 b/libafl_libfuzzer_runtime/build.ps1 index a6d790a87c1..12400f03bf4 100644 --- a/libafl_libfuzzer_runtime/build.ps1 +++ b/libafl_libfuzzer_runtime/build.ps1 @@ -12,17 +12,6 @@ if ($args.Count -eq 0) { $profile = $args[0] } -try { - $nightly_version = Invoke-Expression "cargo +nightly --version" 2>$null - if (-not $nightly_version) { - Write-Host "You must install a recent Rust nightly to build the libafl_libfuzzer runtime!" -ForegroundColor Red - exit 1 - } -} catch { - Write-Host "You must install a recent Rust nightly to build the libafl_libfuzzer runtime!" -ForegroundColor Red - exit 1 -} - Write-Host "Building libafl_libfuzzer runtime with profile '$profile'" -ForegroundColor Green Invoke-Expression "cargo build --profile $profile"