diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 51d5d11d24e..2fc176584ce 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 @@ -522,6 +523,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/inprocess/libafl_libfuzzer_windows + run: cd fuzzers/inprocess/libafl_libfuzzer_windows && just test + windows-libfuzzer-stb-image: runs-on: windows-latest needs: diff --git a/fuzzers/binary_only/frida_libpng/Justfile b/fuzzers/binary_only/frida_libpng/Justfile index 2b607f8ea50..8835f8cc891 100644 --- a/fuzzers/binary_only/frida_libpng/Justfile +++ b/fuzzers/binary_only/frida_libpng/Justfile @@ -47,7 +47,7 @@ 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] diff --git a/fuzzers/inprocess/libafl_libfuzzer_windows/Justfile b/fuzzers/inprocess/libafl_libfuzzer_windows/Justfile new file mode 100644 index 00000000000..f69091ada14 --- /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\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 libFuzzer.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..256e92141fc --- /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 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/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/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)] diff --git a/libafl_libfuzzer/runtime/src/fuzz.rs b/libafl_libfuzzer/runtime/src/fuzz.rs index 8a1d2a43e04..e6f3147d8c7 100644 --- a/libafl_libfuzzer/runtime/src/fuzz.rs +++ b/libafl_libfuzzer/runtime/src/fuzz.rs @@ -1,20 +1,31 @@ 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::{ + fmt::Debug, + fs::File, + io::{Write, stderr, stdout}, + net::TcpListener, + os::fd::AsRawFd, + str::FromStr, +}; +#[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, tui::TuiMonitor}, + monitors::MultiMonitor, stages::StagesTuple, 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}, @@ -22,25 +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(); - 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(); + } + if options.close_fd_mask() & u8::try_from(stderr_fd).unwrap() != 0 { dup2(null_fd, stderr_fd).unwrap(); - } else 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(); - } } } } @@ -87,10 +100,17 @@ 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(()) } +#[cfg(unix)] fn fuzz_single_forking( options: &LibfuzzerOptions, harness: &extern "C" fn(*const u8, usize) -> c_int, @@ -121,9 +141,7 @@ where }) } -/// 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, @@ -134,6 +152,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) @@ -194,22 +215,31 @@ 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(); - fuzz_many_forking(options, harness, shmem_provider, forks, monitor) - } else if forks == 1 { - let monitor = MultiMonitor::new(create_monitor_closure()); - 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) + return fuzz_many_forking(options, harness, shmem_provider, forks, monitor); } - } else if options.tui() { + + // 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); + } + + return fuzz_many_forking(options, harness, shmem_provider, forks, monitor); + } + + #[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"); @@ -217,12 +247,15 @@ pub fn fuzz( .title(options.fuzzer_name()) .enhanced_graphics(true) .build(); - fuzz_many_forking(options, harness, shmem_provider, 1, monitor) - } else { - 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) - }) + 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 21e3b625407..1d90255348f 100644 --- a/libafl_libfuzzer/runtime/src/harness_wrap.cpp +++ b/libafl_libfuzzer/runtime/src/harness_wrap.cpp @@ -8,3 +8,23 @@ extern "C" int libafl_libfuzzer_test_one_input( return -2; // custom code for "we died!" } } + +#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") + + #pragma comment(linker, "/export:LLVMFuzzerRunDriver") + #pragma comment(linker, "/export:__sanitizer_cov_8bit_counters_init") +#endif // _WIN32 diff --git a/libafl_libfuzzer/runtime/src/lib.rs b/libafl_libfuzzer/runtime/src/lib.rs index da826008aea..f48da6130e8 100644 --- a/libafl_libfuzzer/runtime/src/lib.rs +++ b/libafl_libfuzzer/runtime/src/lib.rs @@ -67,8 +67,13 @@ #![allow(clippy::borrow_deref_ref)] use core::ffi::{CStr, c_char, c_int}; -use std::{fs::File, io::stderr, os::fd::RawFd}; +#[cfg(unix)] +use std::{ + os::fd::RawFd, + {fs::File, io::stderr}, +}; +#[cfg(unix)] use env_logger::Target; use libafl::{ Error, @@ -328,11 +333,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 ac3ffcea6f0..a042d4d6333 100644 --- a/libafl_libfuzzer/runtime/src/merge.rs +++ b/libafl_libfuzzer/runtime/src/merge.rs @@ -1,7 +1,7 @@ +use std::{env::temp_dir, ffi::c_int, fs::rename}; +#[cfg(unix)] use std::{ - env::temp_dir, - ffi::c_int, - fs::{File, rename}, + fs::File, io::Write, os::fd::{AsRawFd, FromRawFd}, }; diff --git a/libafl_libfuzzer/runtime/src/options.rs b/libafl_libfuzzer/runtime/src/options.rs index 8a487c20219..ce903dcb101 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 } @@ -318,6 +322,7 @@ impl<'a> LibfuzzerOptionsBuilder<'a> { })?); } "dict" => self.dict = Some(value), + #[cfg(not(windows))] "fork" | "jobs" => { self.forks = Some(parse_or_bail!(name, value, usize)); } @@ -358,6 +363,47 @@ 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:\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); + } _ => { self.unknown.push(arg); } diff --git a/libafl_libfuzzer/runtime/src/tmin.rs b/libafl_libfuzzer/runtime/src/tmin.rs index 7afb05d214d..ccf5a904552 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}, @@ -15,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; @@ -61,15 +66,28 @@ fn minimize_crash_with_mutator>( let mut fuzzer = StdFuzzer::new(QueueScheduler::new(), (), ()); - let shmem_provider = StdShMemProvider::new()?; - let mut executor = InProcessForkExecutor::new( + #[cfg(unix)] + let mut executor = { + let shmem_provider = StdShMemProvider::new()?; + InProcessForkExecutor::new( + &mut harness, + (), + &mut fuzzer, + &mut state, + &mut mgr, + options.timeout(), + shmem_provider, + )? + }; + + #[cfg(windows)] + let mut executor = InProcessExecutor::with_timeout( &mut harness, (), &mut fuzzer, &mut state, &mut mgr, options.timeout(), - shmem_provider, )?; let exit_kind = fuzzer.execute_input(&mut state, &mut executor, &mut mgr, &input)?; 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 new file mode 100644 index 00000000000..12400f03bf4 --- /dev/null +++ b/libafl_libfuzzer_runtime/build.ps1 @@ -0,0 +1,46 @@ +#!/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] +} + +Write-Host "Building libafl_libfuzzer runtime with profile '$profile'" -ForegroundColor Green +Invoke-Expression "cargo build --profile $profile" + +$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 { + 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" + + Copy-Item -Path $targetPath -Destination $outputPath -Force | Out-Null + + if ($LASTEXITCODE -ne 0) { + throw "Failed to copy final library" + } + + Write-Host "Done! Wrote the runtime to '$outputPath'" -ForegroundColor Green +} finally { + Cleanup +} \ No newline at end of file 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"); }