diff --git a/.gitignore b/.gitignore index 6e81bd76db..20c3a58715 100644 --- a/.gitignore +++ b/.gitignore @@ -66,3 +66,5 @@ libafl_nyx/packer .gdb_history # No llvm IR *.ll + +.tar.gz diff --git a/docs/listings/baby_fuzzer/listing-04/src/main.rs b/docs/listings/baby_fuzzer/listing-04/src/main.rs index dae6c112e7..2e514fc5a6 100644 --- a/docs/listings/baby_fuzzer/listing-04/src/main.rs +++ b/docs/listings/baby_fuzzer/listing-04/src/main.rs @@ -2,6 +2,8 @@ extern crate libafl; extern crate libafl_bolts; +use std::path::PathBuf; + use libafl::{ corpus::{InMemoryCorpus, OnDiskCorpus}, events::SimpleEventManager, @@ -13,8 +15,7 @@ use libafl::{ schedulers::QueueScheduler, state::StdState, }; -use libafl_bolts::{current_nanos, rands::StdRand, AsSlice}; -use std::path::PathBuf; +use libafl_bolts::{current_nanos, rands::StdRand, tuples::tuple_list, AsSlice}; /* ANCHOR_END: use */ fn main() { @@ -70,8 +71,14 @@ fn main() { /* ANCHOR: executor */ // Create the executor for an in-process function - let mut executor = InProcessExecutor::new(&mut harness, (), &mut fuzzer, &mut state, &mut mgr) - .expect("Failed to create the Executor"); + let mut executor = InProcessExecutor::new( + &mut harness, + (), + &mut fuzzer, + &mut state, + &mut mgr, + ) + .expect("Failed to create the Executor"); /* ANCHOR_END: executor */ /* ANCHOR: generator */ diff --git a/docs/src/core_concepts/executor.md b/docs/src/core_concepts/executor.md index 463fac72e9..009dc1cab0 100644 --- a/docs/src/core_concepts/executor.md +++ b/docs/src/core_concepts/executor.md @@ -13,8 +13,6 @@ In Rust, we bind this concept to the [`Executor`](https://docs.rs/libafl/latest/ By default, we implement some commonly used Executors such as [`InProcessExecutor`](https://docs.rs/libafl/latest/libafl/executors/inprocess/type.InProcessExecutor.html) in which the target is a harness function providing in-process crash detection. Another Executor is the [`ForkserverExecutor`](https://docs.rs/libafl/latest/libafl/executors/forkserver/struct.ForkserverExecutor.html) that implements an AFL-like mechanism to spawn child processes to fuzz. -A common pattern when creating an Executor is wrapping an existing one, for instance [`TimeoutExecutor`](https://docs.rs/libafl/latest/libafl/executors/timeout/struct.TimeoutExecutor.html) wraps an executor and installs a timeout callback before calling the original `run` function of the wrapped executor. - ## InProcessExecutor Let's begin with the base case; `InProcessExecutor`. This executor executes the harness program (function) inside the fuzzer process. diff --git a/fuzzers/baby_fuzzer_with_forkexecutor/src/main.rs b/fuzzers/baby_fuzzer_with_forkexecutor/src/main.rs index a641a7abf0..cdf80e6fa8 100644 --- a/fuzzers/baby_fuzzer_with_forkexecutor/src/main.rs +++ b/fuzzers/baby_fuzzer_with_forkexecutor/src/main.rs @@ -1,6 +1,6 @@ #[cfg(windows)] use std::ptr::write_volatile; -use std::{path::PathBuf, ptr::write}; +use std::{path::PathBuf, ptr::write, time::Duration}; use libafl::{ corpus::{InMemoryCorpus, OnDiskCorpus}, @@ -110,6 +110,7 @@ pub fn main() { &mut fuzzer, &mut state, &mut mgr, + core::time::Duration::from_millis(5000), shmem_provider, ) .expect("Failed to create the Executor"); diff --git a/fuzzers/backtrace_baby_fuzzers/c_code_with_fork_executor/src/main.rs b/fuzzers/backtrace_baby_fuzzers/c_code_with_fork_executor/src/main.rs index 80df367be7..17667ce3dd 100644 --- a/fuzzers/backtrace_baby_fuzzers/c_code_with_fork_executor/src/main.rs +++ b/fuzzers/backtrace_baby_fuzzers/c_code_with_fork_executor/src/main.rs @@ -1,4 +1,4 @@ -use std::path::PathBuf; +use std::{path::PathBuf, time::Duration}; use libafl::{ corpus::{InMemoryCorpus, OnDiskCorpus}, @@ -98,6 +98,7 @@ pub fn main() { &mut fuzzer, &mut state, &mut mgr, + Duration::from_millis(5000), shmem_provider, ) .expect("Failed to create the Executor"); diff --git a/fuzzers/backtrace_baby_fuzzers/rust_code_with_fork_executor/src/main.rs b/fuzzers/backtrace_baby_fuzzers/rust_code_with_fork_executor/src/main.rs index 193f8d101c..a83aa1359b 100644 --- a/fuzzers/backtrace_baby_fuzzers/rust_code_with_fork_executor/src/main.rs +++ b/fuzzers/backtrace_baby_fuzzers/rust_code_with_fork_executor/src/main.rs @@ -117,6 +117,7 @@ pub fn main() { &mut fuzzer, &mut state, &mut mgr, + core::time::Duration::from_millis(5000), shmem_provider, ) .expect("Failed to create the Executor"); diff --git a/fuzzers/fuzzbench/src/lib.rs b/fuzzers/fuzzbench/src/lib.rs index 2522331087..b6cd2c869a 100644 --- a/fuzzers/fuzzbench/src/lib.rs +++ b/fuzzers/fuzzbench/src/lib.rs @@ -18,7 +18,7 @@ use clap::{Arg, Command}; use libafl::{ corpus::{Corpus, InMemoryOnDiskCorpus, OnDiskCorpus}, events::SimpleRestartingEventManager, - executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor}, + executors::{inprocess::InProcessExecutor, ExitKind}, feedback_or, feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback}, fuzzer::{Fuzzer, StdFuzzer}, @@ -327,29 +327,27 @@ fn fuzz( let mut tracing_harness = harness; // Create the executor for an in-process function with one observer for edge coverage and one for the execution time - let mut executor = TimeoutExecutor::new( - InProcessExecutor::new( - &mut harness, - tuple_list!(edges_observer, time_observer), - &mut fuzzer, - &mut state, - &mut mgr, - )?, + let mut executor = InProcessExecutor::with_timeout( + &mut harness, + tuple_list!(edges_observer, time_observer), + &mut fuzzer, + &mut state, + &mut mgr, timeout, - ); + )?; // Setup a tracing stage in which we log comparisons - let tracing = TracingStage::new(TimeoutExecutor::new( - InProcessExecutor::new( + let tracing = TracingStage::new( + InProcessExecutor::with_timeout( &mut tracing_harness, tuple_list!(cmplog_observer), &mut fuzzer, &mut state, &mut mgr, + timeout * 10, )?, // Give it more time! - timeout * 10, - )); + ); // The order of the stages matter! let mut stages = tuple_list!(calibration, tracing, i2s, power); diff --git a/fuzzers/fuzzbench_fork_qemu/src/fuzzer.rs b/fuzzers/fuzzbench_fork_qemu/src/fuzzer.rs index 71dfd945bd..49bacd7d08 100644 --- a/fuzzers/fuzzbench_fork_qemu/src/fuzzer.rs +++ b/fuzzers/fuzzbench_fork_qemu/src/fuzzer.rs @@ -9,6 +9,7 @@ use std::{ io::{self, Write}, path::PathBuf, process, + time::Duration, }; use clap::{Arg, Command}; @@ -342,6 +343,7 @@ fn fuzz( &mut state, &mut mgr, shmem_provider, + Duration::from_millis(5000), )?; // Show the cmplog observer diff --git a/fuzzers/fuzzbench_qemu/src/fuzzer.rs b/fuzzers/fuzzbench_qemu/src/fuzzer.rs index 38f98de552..b2bedb50cb 100644 --- a/fuzzers/fuzzbench_qemu/src/fuzzer.rs +++ b/fuzzers/fuzzbench_qemu/src/fuzzer.rs @@ -15,7 +15,7 @@ use clap::{Arg, Command}; use libafl::{ corpus::{Corpus, InMemoryOnDiskCorpus, OnDiskCorpus}, events::SimpleRestartingEventManager, - executors::{ExitKind, ShadowExecutor, TimeoutExecutor}, + executors::{ExitKind, ShadowExecutor}, feedback_or, feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback}, fuzzer::{Fuzzer, StdFuzzer}, @@ -351,6 +351,7 @@ fn fuzz( ), ); + // Create the executor for an in-process function with one observer for edge coverage and one for the execution time let executor = QemuExecutor::new( &mut hooks, &mut harness, @@ -358,10 +359,9 @@ fn fuzz( &mut fuzzer, &mut state, &mut mgr, + timeout, )?; - // Create the executor for an in-process function with one observer for edge coverage and one for the execution time - let executor = TimeoutExecutor::new(executor, timeout); // Show the cmplog observer let mut executor = ShadowExecutor::new(executor, tuple_list!(cmplog_observer)); diff --git a/fuzzers/fuzzbench_text/src/lib.rs b/fuzzers/fuzzbench_text/src/lib.rs index 31973c4cff..573ed1ff83 100644 --- a/fuzzers/fuzzbench_text/src/lib.rs +++ b/fuzzers/fuzzbench_text/src/lib.rs @@ -19,7 +19,7 @@ use content_inspector::inspect; use libafl::{ corpus::{Corpus, InMemoryOnDiskCorpus, OnDiskCorpus}, events::SimpleRestartingEventManager, - executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor}, + executors::{inprocess::InProcessExecutor, ExitKind}, feedback_or, feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback}, fuzzer::{Fuzzer, StdFuzzer}, @@ -394,29 +394,24 @@ fn fuzz_binary( let mut tracing_harness = harness; // Create the executor for an in-process function with one observer for edge coverage and one for the execution time - let mut executor = TimeoutExecutor::new( - InProcessExecutor::new( - &mut harness, - tuple_list!(edges_observer, time_observer), - &mut fuzzer, - &mut state, - &mut mgr, - )?, + let mut executor = InProcessExecutor::with_timeout( + &mut harness, + tuple_list!(edges_observer, time_observer), + &mut fuzzer, + &mut state, + &mut mgr, timeout, - ); + )?; // Setup a tracing stage in which we log comparisons - let tracing = TracingStage::new(TimeoutExecutor::new( - InProcessExecutor::new( - &mut tracing_harness, - tuple_list!(cmplog_observer), - &mut fuzzer, - &mut state, - &mut mgr, - )?, - // Give it more time! + let tracing = TracingStage::new(InProcessExecutor::with_timeout( + &mut tracing_harness, + tuple_list!(cmplog_observer), + &mut fuzzer, + &mut state, + &mut mgr, timeout * 10, - )); + )?); // The order of the stages matter! let mut stages = tuple_list!(calibration, tracing, i2s, power); @@ -621,29 +616,24 @@ fn fuzz_text( let generalization = GeneralizationStage::new(&edges_observer); // Create the executor for an in-process function with one observer for edge coverage and one for the execution time - let mut executor = TimeoutExecutor::new( - InProcessExecutor::new( - &mut harness, - tuple_list!(edges_observer, time_observer), - &mut fuzzer, - &mut state, - &mut mgr, - )?, + let mut executor = InProcessExecutor::with_timeout( + &mut harness, + tuple_list!(edges_observer, time_observer), + &mut fuzzer, + &mut state, + &mut mgr, timeout, - ); - + )?; // Setup a tracing stage in which we log comparisons - let tracing = TracingStage::new(TimeoutExecutor::new( - InProcessExecutor::new( - &mut tracing_harness, - tuple_list!(cmplog_observer), - &mut fuzzer, - &mut state, - &mut mgr, - )?, + let tracing = TracingStage::new(InProcessExecutor::with_timeout( + &mut tracing_harness, + tuple_list!(cmplog_observer), + &mut fuzzer, + &mut state, + &mut mgr, // Give it more time! timeout * 10, - )); + )?); // The order of the stages matter! let mut stages = tuple_list!(generalization, calibration, tracing, i2s, power, grimoire); diff --git a/fuzzers/libafl_atheris/src/lib.rs b/fuzzers/libafl_atheris/src/lib.rs index 63ad67f44c..2645713e5d 100644 --- a/fuzzers/libafl_atheris/src/lib.rs +++ b/fuzzers/libafl_atheris/src/lib.rs @@ -14,7 +14,7 @@ use clap::{Arg, ArgAction, Command}; use libafl::{ corpus::{Corpus, InMemoryCorpus, OnDiskCorpus}, events::{launcher::Launcher, EventConfig}, - executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor}, + executors::{inprocess::InProcessExecutor, ExitKind}, feedback_or, feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, StdFuzzer}, @@ -197,16 +197,14 @@ pub extern "C" fn LLVMFuzzerRunDriver( }; // Create the executor for an in-process function with one observer for edge coverage and one for the execution time - let mut executor = TimeoutExecutor::new( - InProcessExecutor::new( - &mut harness, - tuple_list!(edges_observer, time_observer), - &mut fuzzer, - &mut state, - &mut mgr, - )?, + let mut executor = InProcessExecutor::with_timeout( + &mut harness, + tuple_list!(edges_observer, time_observer), + &mut fuzzer, + &mut state, + &mut mgr, Duration::from_millis(timeout_ms), - ); + )?; // Secondary harness due to mut ownership let mut harness = |input: &BytesInput| { diff --git a/fuzzers/libfuzzer_libpng/src/lib.rs b/fuzzers/libfuzzer_libpng/src/lib.rs index 0605ec288d..e1e5cb00a6 100644 --- a/fuzzers/libfuzzer_libpng/src/lib.rs +++ b/fuzzers/libfuzzer_libpng/src/lib.rs @@ -12,7 +12,7 @@ use std::{env, path::PathBuf}; use libafl::{ corpus::{Corpus, InMemoryCorpus, OnDiskCorpus}, events::{setup_restarting_mgr_std, EventConfig, EventRestarter}, - executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor}, + executors::{inprocess::InProcessExecutor, ExitKind}, feedback_or, feedback_or_fast, feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, StdFuzzer}, @@ -173,17 +173,15 @@ fn fuzz(corpus_dirs: &[PathBuf], objective_dir: PathBuf, broker_port: u16) -> Re }; // Create the executor for an in-process function with one observer for edge coverage and one for the execution time - let mut executor = TimeoutExecutor::new( - InProcessExecutor::new( - &mut harness, - tuple_list!(edges_observer, time_observer), - &mut fuzzer, - &mut state, - &mut restarting_mgr, - )?, - // 10 seconds timeout + let mut executor = InProcessExecutor::with_timeout( + &mut harness, + tuple_list!(edges_observer, time_observer), + &mut fuzzer, + &mut state, + &mut restarting_mgr, Duration::new(10, 0), - ); + )?; + // 10 seconds timeout // The actual target run starts here. // Call LLVMFUzzerInitialize() if present. diff --git a/fuzzers/libfuzzer_libpng_accounting/src/lib.rs b/fuzzers/libfuzzer_libpng_accounting/src/lib.rs index c91ec89663..beb6fc0d45 100644 --- a/fuzzers/libfuzzer_libpng_accounting/src/lib.rs +++ b/fuzzers/libfuzzer_libpng_accounting/src/lib.rs @@ -13,7 +13,7 @@ use clap::Parser; use libafl::{ corpus::{Corpus, InMemoryCorpus, OnDiskCorpus}, events::{EventConfig, Launcher}, - executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor}, + executors::{inprocess::InProcessExecutor, ExitKind}, feedback_or, feedback_or_fast, feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, StdFuzzer}, @@ -205,17 +205,14 @@ pub extern "C" fn libafl_main() { }; // Create the executor for an in-process function with one observer for edge coverage and one for the execution time - let mut executor = TimeoutExecutor::new( - InProcessExecutor::new( - &mut harness, - tuple_list!(edges_observer, time_observer), - &mut fuzzer, - &mut state, - &mut restarting_mgr, - )?, - // 10 seconds timeout + let mut executor = InProcessExecutor::with_timeout( + &mut harness, + tuple_list!(edges_observer, time_observer), + &mut fuzzer, + &mut state, + &mut restarting_mgr, opt.timeout, - ); + )?; // The actual target run starts here. // Call LLVMFUzzerInitialize() if present. diff --git a/fuzzers/libfuzzer_libpng_aflpp_ui/src/lib.rs b/fuzzers/libfuzzer_libpng_aflpp_ui/src/lib.rs index 405444f1ac..a28d3ce6f2 100644 --- a/fuzzers/libfuzzer_libpng_aflpp_ui/src/lib.rs +++ b/fuzzers/libfuzzer_libpng_aflpp_ui/src/lib.rs @@ -12,7 +12,7 @@ use std::{env, path::PathBuf}; use libafl::{ corpus::{Corpus, InMemoryOnDiskCorpus, OnDiskCorpus}, events::{setup_restarting_mgr_std, EventConfig, EventRestarter}, - executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor}, + executors::{inprocess::InProcessExecutor, ExitKind}, feedback_or, feedback_or_fast, feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, StdFuzzer}, @@ -189,17 +189,14 @@ fn fuzz( }; // Create the executor for an in-process function with one observer for edge coverage and one for the execution time - let mut executor = TimeoutExecutor::new( - InProcessExecutor::new( - &mut harness, - tuple_list!(edges_observer, time_observer), - &mut fuzzer, - &mut state, - &mut restarting_mgr, - )?, - // 10 seconds timeout + let mut executor = InProcessExecutor::with_timeout( + &mut harness, + tuple_list!(edges_observer, time_observer), + &mut fuzzer, + &mut state, + &mut restarting_mgr, Duration::new(10, 0), - ); + )?; // The actual target run starts here. // Call LLVMFUzzerInitialize() if present. diff --git a/fuzzers/libfuzzer_libpng_centralized/src/lib.rs b/fuzzers/libfuzzer_libpng_centralized/src/lib.rs index 4a1bbe5360..d799e81196 100644 --- a/fuzzers/libfuzzer_libpng_centralized/src/lib.rs +++ b/fuzzers/libfuzzer_libpng_centralized/src/lib.rs @@ -13,7 +13,7 @@ use clap::{self, Parser}; use libafl::{ corpus::{Corpus, InMemoryCorpus, OnDiskCorpus}, events::{launcher::CentralizedLauncher, EventConfig}, - executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor}, + executors::{inprocess::InProcessExecutor, ExitKind}, feedback_or, feedback_or_fast, feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, StdFuzzer}, @@ -200,21 +200,25 @@ pub extern "C" fn libafl_main() { }; // Create the executor for an in-process function with one observer for edge coverage and one for the execution time - let executor = InProcessExecutor::new( + #[cfg(target_os = "linux")] + let mut executor = InProcessExecutor::batched_timeouts( &mut harness, tuple_list!(edges_observer, time_observer), &mut fuzzer, &mut state, &mut mgr, + opt.timeout, )?; - // Wrap the executor with a timeout - #[cfg(target_os = "linux")] - let mut executor = TimeoutExecutor::batch_mode(executor, opt.timeout); - - // Wrap the executor with a timeout #[cfg(not(target_os = "linux"))] - let mut executor = TimeoutExecutor::new(executor, opt.timeout); + let mut executor = InProcessExecutor::with_timeout( + &mut harness, + tuple_list!(edges_observer, time_observer), + &mut fuzzer, + &mut state, + &mut mgr, + opt.timeout, + )?; // The actual target run starts here. // Call LLVMFUzzerInitialize() if present. diff --git a/fuzzers/libfuzzer_libpng_cmin/src/lib.rs b/fuzzers/libfuzzer_libpng_cmin/src/lib.rs index ad88f42e47..59d9a449b9 100644 --- a/fuzzers/libfuzzer_libpng_cmin/src/lib.rs +++ b/fuzzers/libfuzzer_libpng_cmin/src/lib.rs @@ -15,7 +15,7 @@ use libafl::{ Corpus, InMemoryCorpus, OnDiskCorpus, }, events::{setup_restarting_mgr_std, EventConfig, EventFirer, EventRestarter, LogSeverity}, - executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor}, + executors::{inprocess::InProcessExecutor, ExitKind}, feedback_or, feedback_or_fast, feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, StdFuzzer}, @@ -172,17 +172,14 @@ fn fuzz(corpus_dirs: &[PathBuf], objective_dir: PathBuf, broker_port: u16) -> Re }; // Create the executor for an in-process function with one observer for edge coverage and one for the execution time - let mut executor = TimeoutExecutor::new( - InProcessExecutor::new( - &mut harness, - tuple_list!(edges_observer, time_observer), - &mut fuzzer, - &mut state, - &mut restarting_mgr, - )?, - // 10 seconds timeout + let mut executor = InProcessExecutor::with_timeout( + &mut harness, + tuple_list!(edges_observer, time_observer), + &mut fuzzer, + &mut state, + &mut restarting_mgr, Duration::new(10, 0), - ); + )?; // The actual target run starts here. // Call LLVMFUzzerInitialize() if present. diff --git a/fuzzers/libfuzzer_libpng_ctx/src/lib.rs b/fuzzers/libfuzzer_libpng_ctx/src/lib.rs index 01e897a955..2e422fb71c 100644 --- a/fuzzers/libfuzzer_libpng_ctx/src/lib.rs +++ b/fuzzers/libfuzzer_libpng_ctx/src/lib.rs @@ -13,7 +13,7 @@ use clap::{self, Parser}; use libafl::{ corpus::{Corpus, InMemoryCorpus, OnDiskCorpus}, events::{launcher::Launcher, EventConfig}, - executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor}, + executors::{inprocess::InProcessExecutor, ExitKind}, feedback_or, feedback_or_fast, feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, StdFuzzer}, @@ -196,17 +196,14 @@ pub extern "C" fn libafl_main() { }; // Create the executor for an in-process function with one observer for edge coverage and one for the execution time - let mut executor = TimeoutExecutor::new( - InProcessExecutor::new( - &mut harness, - tuple_list!(edges_observer, time_observer), - &mut fuzzer, - &mut state, - &mut restarting_mgr, - )?, - // 10 seconds timeout + let mut executor = InProcessExecutor::with_timeout( + &mut harness, + tuple_list!(edges_observer, time_observer), + &mut fuzzer, + &mut state, + &mut restarting_mgr, opt.timeout, - ); + )?; // The actual target run starts here. // Call LLVMFUzzerInitialize() if present. diff --git a/fuzzers/libfuzzer_libpng_launcher/src/lib.rs b/fuzzers/libfuzzer_libpng_launcher/src/lib.rs index 60ef106ba8..3b452bf681 100644 --- a/fuzzers/libfuzzer_libpng_launcher/src/lib.rs +++ b/fuzzers/libfuzzer_libpng_launcher/src/lib.rs @@ -13,7 +13,7 @@ use clap::{self, Parser}; use libafl::{ corpus::{Corpus, InMemoryCorpus, OnDiskCorpus}, events::{launcher::Launcher, EventConfig}, - executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor}, + executors::{inprocess::InProcessExecutor, ExitKind}, feedback_or, feedback_or_fast, feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, StdFuzzer}, @@ -201,21 +201,25 @@ pub extern "C" fn libafl_main() { }; // Create the executor for an in-process function with one observer for edge coverage and one for the execution time - let executor = InProcessExecutor::new( + #[cfg(target_os = "linux")] + let mut executor = InProcessExecutor::batched_timeouts( &mut harness, tuple_list!(edges_observer, time_observer), &mut fuzzer, &mut state, &mut restarting_mgr, + opt.timeout, )?; - // Wrap the executor with a timeout - #[cfg(target_os = "linux")] - let mut executor = TimeoutExecutor::batch_mode(executor, opt.timeout); - - // Wrap the executor with a timeout #[cfg(not(target_os = "linux"))] - let mut executor = TimeoutExecutor::new(executor, opt.timeout); + let mut executor = InProcessExecutor::with_timeout( + &mut harness, + tuple_list!(edges_observer, time_observer), + &mut fuzzer, + &mut state, + &mut restarting_mgr, + opt.timeout, + )?; // The actual target run starts here. // Call LLVMFUzzerInitialize() if present. diff --git a/fuzzers/libfuzzer_libpng_norestart/src/lib.rs b/fuzzers/libfuzzer_libpng_norestart/src/lib.rs index 1aeb3cd914..a9de4ae99b 100644 --- a/fuzzers/libfuzzer_libpng_norestart/src/lib.rs +++ b/fuzzers/libfuzzer_libpng_norestart/src/lib.rs @@ -13,7 +13,7 @@ use clap::{self, Parser}; use libafl::{ corpus::{Corpus, InMemoryOnDiskCorpus, OnDiskCorpus}, events::{launcher::Launcher, EventConfig, EventRestarter, LlmpRestartingEventManager}, - executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor}, + executors::{inprocess::InProcessExecutor, ExitKind}, feedback_or, feedback_or_fast, feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, StdFuzzer}, @@ -222,18 +222,14 @@ pub extern "C" fn libafl_main() { }; // Create the executor for an in-process function with one observer for edge coverage and one for the execution time - let mut executor = TimeoutExecutor::new( - InProcessExecutor::new( - &mut harness, - tuple_list!(edges_observer, time_observer), - &mut fuzzer, - &mut state, - &mut restarting_mgr, - )?, - // 10 seconds timeout + let mut executor = InProcessExecutor::with_timeout( + &mut harness, + tuple_list!(edges_observer, time_observer), + &mut fuzzer, + &mut state, + &mut restarting_mgr, opt.timeout, - ); - + )?; // The actual target run starts here. // Call LLVMFUzzerInitialize() if present. let args: Vec = env::args().collect(); diff --git a/fuzzers/libfuzzer_libpng_tcp_manager/src/lib.rs b/fuzzers/libfuzzer_libpng_tcp_manager/src/lib.rs index 3125b062ac..72ca166bd5 100644 --- a/fuzzers/libfuzzer_libpng_tcp_manager/src/lib.rs +++ b/fuzzers/libfuzzer_libpng_tcp_manager/src/lib.rs @@ -12,7 +12,7 @@ use std::{env, path::PathBuf}; use libafl::{ corpus::{Corpus, InMemoryCorpus, OnDiskCorpus}, events::{tcp::setup_restarting_mgr_tcp, EventConfig, EventRestarter}, - executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor}, + executors::{inprocess::InProcessExecutor, ExitKind}, feedback_or, feedback_or_fast, feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, StdFuzzer}, @@ -171,17 +171,14 @@ fn fuzz(corpus_dirs: &[PathBuf], objective_dir: PathBuf, broker_port: u16) -> Re }; // Create the executor for an in-process function with one observer for edge coverage and one for the execution time - let mut executor = TimeoutExecutor::new( - InProcessExecutor::new( - &mut harness, - tuple_list!(edges_observer, time_observer), - &mut fuzzer, - &mut state, - &mut restarting_mgr, - )?, - // 10 seconds timeout + let mut executor = InProcessExecutor::with_timeout( + &mut harness, + tuple_list!(edges_observer, time_observer), + &mut fuzzer, + &mut state, + &mut restarting_mgr, Duration::new(10, 0), - ); + )?; // The actual target run starts here. // Call LLVMFUzzerInitialize() if present. diff --git a/fuzzers/libfuzzer_windows_asan/src/lib.rs b/fuzzers/libfuzzer_windows_asan/src/lib.rs index fbc735d484..2f0eeab1da 100644 --- a/fuzzers/libfuzzer_windows_asan/src/lib.rs +++ b/fuzzers/libfuzzer_windows_asan/src/lib.rs @@ -4,7 +4,7 @@ use std::{env, path::PathBuf}; use libafl::{ corpus::{Corpus, InMemoryCorpus, OnDiskCorpus}, events::{setup_restarting_mgr_std, EventConfig, EventRestarter}, - executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor}, + executors::{inprocess::InProcessExecutor, ExitKind}, feedback_or, feedback_or_fast, feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, StdFuzzer}, @@ -130,17 +130,14 @@ fn fuzz(corpus_dirs: &[PathBuf], objective_dir: PathBuf, broker_port: u16) -> Re }; // Create the executor for an in-process function with one observer for edge coverage and one for the execution time - let mut executor = TimeoutExecutor::new( - InProcessExecutor::new( - &mut harness, - tuple_list!(edges_observer, time_observer), - &mut fuzzer, - &mut state, - &mut restarting_mgr, - )?, - // 10 seconds timeout + let mut executor = InProcessExecutor::with_timeout( + &mut harness, + tuple_list!(edges_observer, time_observer), + &mut fuzzer, + &mut state, + &mut restarting_mgr, Duration::new(10, 0), - ); + )?; // Initialize ASAN, call this before any ASAN crashes can occur (directly after initializing executor e.g.) #[cfg(windows)] diff --git a/fuzzers/push_stage_harness/src/main.rs b/fuzzers/push_stage_harness/src/main.rs index 40b4886b65..201a3a860c 100644 --- a/fuzzers/push_stage_harness/src/main.rs +++ b/fuzzers/push_stage_harness/src/main.rs @@ -70,7 +70,7 @@ pub fn main() { let mut scheduler = QueueScheduler::new(); // Create the executor for an in-process function with just one observer - //let mut executor = InProcessExecutor::new(&mut harness, &mut fuzzer, &mut state, &mut mgr) + //let mut executor = InProcessExecutor::new(tuple_list!(), &mut harness, &mut fuzzer, &mut state, &mut mgr) // .expect("Failed to create the Executor"); let testcase = Testcase::new(BytesInput::new(b"aaaa".to_vec())); diff --git a/fuzzers/qemu_cmin/src/fuzzer.rs b/fuzzers/qemu_cmin/src/fuzzer.rs index e2a69ad254..6a7e5e5133 100644 --- a/fuzzers/qemu_cmin/src/fuzzer.rs +++ b/fuzzers/qemu_cmin/src/fuzzer.rs @@ -228,6 +228,7 @@ pub fn fuzz() -> Result<(), Error> { &mut state, &mut mgr, shmem_provider, + core::time::Duration::from_millis(5000), )?; println!("Importing {} seeds...", files.len()); diff --git a/fuzzers/qemu_coverage/src/fuzzer.rs b/fuzzers/qemu_coverage/src/fuzzer.rs index 4632e75fd4..334c5dcc72 100644 --- a/fuzzers/qemu_coverage/src/fuzzer.rs +++ b/fuzzers/qemu_coverage/src/fuzzer.rs @@ -9,7 +9,7 @@ use clap::{builder::Str, Parser}; use libafl::{ corpus::{Corpus, NopCorpus}, events::{launcher::Launcher, EventConfig, EventRestarter}, - executors::{ExitKind, TimeoutExecutor}, + executors::ExitKind, fuzzer::StdFuzzer, inputs::{BytesInput, HasTargetBytes}, monitors::MultiMonitor, @@ -35,6 +35,11 @@ use rangemap::RangeMap; #[derive(Default)] pub struct Version; +/// Parse a millis string to a [`Duration`]. Used for arg parsing. +fn timeout_from_millis_str(time: &str) -> Result { + Ok(Duration::from_millis(time.parse()?)) +} + impl From for Str { fn from(_: Version) -> Str { let version = [ @@ -73,8 +78,8 @@ pub struct FuzzerOptions { #[arg(long, help = "Input directory")] input: String, - #[arg(long, help = "Timeout in seconds", default_value_t = 1_u64)] - timeout: u64, + #[arg(long, help = "Timeout in seconds", default_value = "5000", value_parser = timeout_from_millis_str)] + timeout: Duration, #[arg(long = "port", help = "Broker port", default_value_t = 1337_u16)] port: u16, @@ -245,18 +250,17 @@ pub fn fuzz() { )), ); - let executor = QemuExecutor::new( + let mut executor = QemuExecutor::new( &mut hooks, &mut harness, (), &mut fuzzer, &mut state, &mut mgr, + options.timeout, ) .expect("Failed to create QemuExecutor"); - let mut executor = TimeoutExecutor::new(executor, Duration::from_secs(options.timeout)); - if state.must_load_initial_inputs() { state .load_initial_inputs_by_filenames(&mut fuzzer, &mut executor, &mut mgr, &files) diff --git a/fuzzers/qemu_launcher/injection_test/sqltest.c b/fuzzers/qemu_launcher/injection_test/sqltest.c index 94b14de292..88bdb46661 100644 --- a/fuzzers/qemu_launcher/injection_test/sqltest.c +++ b/fuzzers/qemu_launcher/injection_test/sqltest.c @@ -18,7 +18,6 @@ int LLVMFuzzerTestOneInput(char *data, size_t len) { char *err_msg = 0, query[1024]; if (data[0] % 2) { - int rc = sqlite3_open_v2("example.db", &db, SQLITE_OPEN_READONLY, 0); if (rc != SQLITE_OK) { fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db)); @@ -33,9 +32,7 @@ int LLVMFuzzerTestOneInput(char *data, size_t len) { rc = sqlite3_exec(db, query, callback, 0, &err_msg); - if (rc != SQLITE_OK) { - sqlite3_free(err_msg); - } + if (rc != SQLITE_OK) { sqlite3_free(err_msg); } sqlite3_close(db); diff --git a/fuzzers/qemu_launcher/src/client.rs b/fuzzers/qemu_launcher/src/client.rs index 4792d9673c..534db00b76 100644 --- a/fuzzers/qemu_launcher/src/client.rs +++ b/fuzzers/qemu_launcher/src/client.rs @@ -10,6 +10,8 @@ use libafl::{ use libafl_bolts::{ core_affinity::CoreId, rands::StdRand, shmem::StdShMemProvider, tuples::tuple_list, }; +#[cfg(feature = "injections")] +use libafl_qemu::injections::QemuInjectionHelper; use libafl_qemu::{ asan::{init_with_asan, QemuAsanHelper}, cmplog::QemuCmpLogHelper, @@ -18,9 +20,6 @@ use libafl_qemu::{ ArchExtras, Emulator, GuestAddr, QemuInstrumentationAddressRangeFilter, }; -#[cfg(feature = "injections")] -use libafl_qemu::injections::QemuInjectionHelper; - use crate::{instance::Instance, options::FuzzerOptions}; #[allow(clippy::module_name_repetitions)] diff --git a/fuzzers/qemu_launcher/src/instance.rs b/fuzzers/qemu_launcher/src/instance.rs index 27a18e59ce..51b297aac8 100644 --- a/fuzzers/qemu_launcher/src/instance.rs +++ b/fuzzers/qemu_launcher/src/instance.rs @@ -4,7 +4,7 @@ use std::process; use libafl::{ corpus::{Corpus, InMemoryOnDiskCorpus, OnDiskCorpus}, events::{EventRestarter, LlmpRestartingEventManager}, - executors::{ShadowExecutor, TimeoutExecutor}, + executors::ShadowExecutor, feedback_or, feedback_or_fast, feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, fuzzer::{Evaluator, Fuzzer, StdFuzzer}, @@ -150,11 +150,9 @@ impl<'a> Instance<'a> { &mut fuzzer, &mut state, &mut self.mgr, + self.options.timeout, )?; - // Wrap the executor to keep track of the timeout - let executor = TimeoutExecutor::new(executor, self.options.timeout); - // Create an observation channel using cmplog map let cmplog_observer = CmpLogObserver::new("cmplog", true); @@ -183,18 +181,16 @@ impl<'a> Instance<'a> { self.fuzz(&mut state, &mut fuzzer, &mut executor, &mut stages) } else { // Create a QEMU in-process executor - let executor = QemuExecutor::new( + let mut executor = QemuExecutor::new( &mut hooks, &mut harness, observers, &mut fuzzer, &mut state, &mut self.mgr, + self.options.timeout, )?; - // Wrap the executor to keep track of the timeout - let mut executor = TimeoutExecutor::new(executor, self.options.timeout); - // Setup an havoc mutator with a mutational stage let mutator = StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations())); let mut stages = tuple_list!(StdMutationalStage::new(mutator)); diff --git a/fuzzers/qemu_systemmode/src/fuzzer.rs b/fuzzers/qemu_systemmode/src/fuzzer.rs index 5229eba8f7..62b9c1010f 100644 --- a/fuzzers/qemu_systemmode/src/fuzzer.rs +++ b/fuzzers/qemu_systemmode/src/fuzzer.rs @@ -6,7 +6,7 @@ use std::{env, path::PathBuf, process}; use libafl::{ corpus::{Corpus, InMemoryCorpus, OnDiskCorpus}, events::{launcher::Launcher, EventConfig}, - executors::{ExitKind, TimeoutExecutor}, + executors::ExitKind, feedback_or, feedback_or_fast, feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, StdFuzzer}, @@ -202,15 +202,13 @@ pub fn fuzz() { &mut fuzzer, &mut state, &mut mgr, + timeout, ) .expect("Failed to create QemuExecutor"); // Instead of calling the timeout handler and restart the process, trigger a breakpoint ASAP executor.break_on_timeout(); - // Wrap the executor to keep track of the timeout - let mut executor = TimeoutExecutor::new(executor, timeout); - if state.must_load_initial_inputs() { state .load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &corpus_dirs) diff --git a/fuzzers/tutorial/src/lib.rs b/fuzzers/tutorial/src/lib.rs index 53552376f5..1c7fe08fe7 100644 --- a/fuzzers/tutorial/src/lib.rs +++ b/fuzzers/tutorial/src/lib.rs @@ -9,7 +9,7 @@ use std::{env, path::PathBuf}; use libafl::{ corpus::{Corpus, InMemoryCorpus, OnDiskCorpus}, events::{setup_restarting_mgr_std, EventConfig, EventRestarter}, - executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor}, + executors::{inprocess::InProcessExecutor, ExitKind}, feedback_or, feedback_or_fast, feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, fuzzer::StdFuzzer, @@ -142,17 +142,14 @@ fn fuzz(corpus_dirs: &[PathBuf], objective_dir: PathBuf, broker_port: u16) -> Re let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); // Create the executor for an in-process function with one observer for edge coverage and one for the execution time - let mut executor = TimeoutExecutor::new( - InProcessExecutor::new( - &mut harness, - tuple_list!(edges_observer, time_observer), - &mut fuzzer, - &mut state, - &mut restarting_mgr, - )?, - // 10 seconds timeout + let mut executor = InProcessExecutor::with_timeout( + &mut harness, + tuple_list!(edges_observer, time_observer), + &mut fuzzer, + &mut state, + &mut restarting_mgr, Duration::new(10, 0), - ); + )?; // The actual target run starts here. // Call LLVMFUzzerInitialize() if present. diff --git a/libafl/src/executors/combined.rs b/libafl/src/executors/combined.rs index c9015f3812..eeff368eed 100644 --- a/libafl/src/executors/combined.rs +++ b/libafl/src/executors/combined.rs @@ -57,10 +57,7 @@ where ) -> Result { *state.executions_mut() += 1; - let ret = self.primary.run_target(fuzzer, state, mgr, input); - self.primary.post_run_reset(); - self.secondary.post_run_reset(); - ret + self.primary.run_target(fuzzer, state, mgr, input) } } diff --git a/libafl/src/executors/differential.rs b/libafl/src/executors/differential.rs index a657c0eddc..8da268f4cb 100644 --- a/libafl/src/executors/differential.rs +++ b/libafl/src/executors/differential.rs @@ -77,7 +77,6 @@ where .pre_observe_first_all(observers.primary.as_mut())?; observers.primary.as_mut().pre_exec_all(state, input)?; let ret1 = self.primary.run_target(fuzzer, state, mgr, input)?; - self.primary.post_run_reset(); observers .primary .as_mut() @@ -90,7 +89,6 @@ where .pre_observe_second_all(observers.secondary.as_mut())?; observers.secondary.as_mut().pre_exec_all(state, input)?; let ret2 = self.secondary.run_target(fuzzer, state, mgr, input)?; - self.secondary.post_run_reset(); observers .secondary .as_mut() diff --git a/libafl/src/executors/hooks/inprocess.rs b/libafl/src/executors/hooks/inprocess.rs new file mode 100644 index 0000000000..98a0b125d3 --- /dev/null +++ b/libafl/src/executors/hooks/inprocess.rs @@ -0,0 +1,471 @@ +//! The hook for `InProcessExecutor` +#[cfg(any(unix, all(windows, feature = "std")))] +use core::sync::atomic::{compiler_fence, Ordering}; +use core::{ + ffi::c_void, + ptr::{self, null_mut}, + time::Duration, +}; +#[cfg(all(target_os = "linux", feature = "std"))] +use core::{mem::zeroed, ptr::addr_of}; + +#[cfg(all(target_os = "linux", feature = "std"))] +use libafl_bolts::current_time; +#[cfg(all(unix, feature = "std", not(miri)))] +use libafl_bolts::os::unix_signals::setup_signal_handler; +#[cfg(all(windows, feature = "std"))] +use libafl_bolts::os::windows_exceptions::setup_exception_handler; +#[cfg(all(windows, feature = "std"))] +use windows::Win32::System::Threading::{CRITICAL_SECTION, PTP_TIMER}; + +#[cfg(feature = "std")] +use crate::executors::hooks::timer::TimerStruct; +#[cfg(all(unix, feature = "std"))] +use crate::executors::hooks::unix::unix_signal_handler; +#[cfg(windows)] +use crate::state::State; +use crate::{ + events::{EventFirer, EventRestarter}, + executors::{hooks::ExecutorHook, inprocess::HasInProcessHooks, Executor, HasObservers}, + feedbacks::Feedback, + state::{HasCorpus, HasExecutions, HasSolutions}, + Error, HasObjective, +}; +/// The inmem executor's handlers. +#[allow(missing_debug_implementations)] +pub struct InProcessHooks { + /// On crash C function pointer + #[cfg(feature = "std")] + pub crash_handler: *const c_void, + /// On timeout C function pointer + #[cfg(feature = "std")] + pub timeout_handler: *const c_void, + /// TImer struct + #[cfg(feature = "std")] + pub timer: TimerStruct, +} + +/// Any hooks that is about timeout +pub trait HasTimeout { + /// Return ref to timer + #[cfg(feature = "std")] + fn timer(&self) -> &TimerStruct; + /// Return mut ref to timer + #[cfg(feature = "std")] + fn timer_mut(&mut self) -> &mut TimerStruct; + #[cfg(all(feature = "std", windows))] + /// The timer object + #[cfg(all(feature = "std", windows))] + fn ptp_timer(&self) -> &PTP_TIMER; + #[cfg(all(feature = "std", windows))] + /// The critical section + fn critical(&self) -> &CRITICAL_SECTION; + #[cfg(all(feature = "std", windows))] + /// The critical section (mut) + fn critical_mut(&mut self) -> &mut CRITICAL_SECTION; + #[cfg(all(feature = "std", windows))] + /// The timeout in milli sec + #[cfg(all(feature = "std", windows))] + fn milli_sec(&self) -> i64; + #[cfg(all(feature = "std", windows))] + /// The timeout in milli sec (mut ref) + fn millis_sec_mut(&mut self) -> &mut i64; + #[cfg(not(all(unix, feature = "std")))] + /// Handle timeout for batch mode timeout + fn handle_timeout(&mut self) -> bool; + #[cfg(all(unix, feature = "std"))] + /// Handle timeout for batch mode timeout + fn handle_timeout(&mut self, data: &mut InProcessExecutorHandlerData) -> bool; +} + +impl HasTimeout for InProcessHooks { + #[cfg(feature = "std")] + fn timer(&self) -> &TimerStruct { + &self.timer + } + #[cfg(feature = "std")] + fn timer_mut(&mut self) -> &mut TimerStruct { + &mut self.timer + } + + #[cfg(all(feature = "std", windows))] + fn ptp_timer(&self) -> &PTP_TIMER { + self.timer().ptp_timer() + } + + #[cfg(all(feature = "std", windows))] + fn critical(&self) -> &CRITICAL_SECTION { + self.timer().critical() + } + + #[cfg(all(feature = "std", windows))] + fn critical_mut(&mut self) -> &mut CRITICAL_SECTION { + self.timer_mut().critical_mut() + } + + #[cfg(all(feature = "std", windows))] + fn milli_sec(&self) -> i64 { + self.timer().milli_sec() + } + + #[cfg(all(feature = "std", windows))] + fn millis_sec_mut(&mut self) -> &mut i64 { + self.timer_mut().milli_sec_mut() + } + + #[cfg(not(all(unix, feature = "std")))] + fn handle_timeout(&mut self) -> bool { + false + } + + #[cfg(all(unix, feature = "std"))] + #[allow(unused)] + fn handle_timeout(&mut self, data: &mut InProcessExecutorHandlerData) -> bool { + #[cfg(not(target_os = "linux"))] + { + return false; + } + + #[cfg(target_os = "linux")] + { + if !self.timer().batch_mode { + return false; + } + //eprintln!("handle_timeout {:?} {}", self.avg_exec_time, self.avg_mul_k); + let cur_time = current_time(); + if !data.is_valid() { + // outside the target + unsafe { + let disarmed: libc::itimerspec = zeroed(); + libc::timer_settime( + self.timer_mut().timerid, + 0, + addr_of!(disarmed), + null_mut(), + ); + } + let elapsed = cur_time - self.timer().tmout_start_time; + // set timer the next exec + if self.timer().executions > 0 { + self.timer_mut().avg_exec_time = elapsed / self.timer().executions; + self.timer_mut().executions = 0; + } + self.timer_mut().avg_mul_k += 1; + self.timer_mut().last_signal_time = cur_time; + return true; + } + + let elapsed_run = cur_time - self.timer_mut().start_time; + if elapsed_run < self.timer_mut().exec_tmout { + // fp, reset timeout + unsafe { + libc::timer_settime( + self.timer_mut().timerid, + 0, + addr_of!(self.timer_mut().itimerspec), + null_mut(), + ); + } + if self.timer().executions > 0 { + let elapsed = cur_time - self.timer_mut().tmout_start_time; + self.timer_mut().avg_exec_time = elapsed / self.timer().executions; + self.timer_mut().executions = 0; // It will be 1 when the exec finish + } + self.timer_mut().tmout_start_time = current_time(); + self.timer_mut().avg_mul_k += 1; + self.timer_mut().last_signal_time = cur_time; + true + } else { + false + } + } + } +} + +impl ExecutorHook for InProcessHooks { + fn init(&mut self, _state: &mut S) {} + + /// Call before running a target. + #[allow(clippy::unused_self)] + #[allow(unused_variables)] + fn pre_exec(&mut self, fuzzer: &mut Z, state: &mut S, mgr: &mut EM, input: &I) { + #[cfg(feature = "std")] + { + let data = unsafe { &mut GLOBAL_STATE }; + data.crash_handler = self.crash_handler; + data.timeout_handler = self.timeout_handler; + } + + #[cfg(feature = "std")] + self.timer_mut().set_timer(); + } + + /// Call after running a target. + #[allow(clippy::unused_self)] + fn post_exec( + &mut self, + _fuzzer: &mut Z, + _state: &mut S, + _mgr: &mut EM, + _input: &I, + ) { + // let _data = unsafe { &mut GLOBAL_STATE }; + // timeout stuff + #[cfg(feature = "std")] + self.timer_mut().unset_timer(); + } +} + +impl InProcessHooks { + /// Create new [`InProcessHooks`]. + #[cfg(unix)] + #[allow(unused_variables)] + pub fn new(exec_tmout: Duration) -> Result + where + E: Executor + HasObservers + HasInProcessHooks, + EM: EventFirer + EventRestarter, + OF: Feedback, + E::State: HasExecutions + HasSolutions + HasCorpus, + Z: HasObjective, + { + #[cfg_attr(miri, allow(unused_variables))] + unsafe { + let data = &mut GLOBAL_STATE; + #[cfg(feature = "std")] + unix_signal_handler::setup_panic_hook::(); + #[cfg(all(not(miri), unix, feature = "std"))] + setup_signal_handler(data)?; + compiler_fence(Ordering::SeqCst); + Ok(Self { + #[cfg(feature = "std")] + crash_handler: unix_signal_handler::inproc_crash_handler:: + as *const c_void, + #[cfg(feature = "std")] + timeout_handler: unix_signal_handler::inproc_timeout_handler:: + as *const _, + #[cfg(feature = "std")] + timer: TimerStruct::new(exec_tmout), + }) + } + } + + /// Create new [`InProcessHooks`]. + #[cfg(windows)] + #[allow(unused)] + pub fn new(exec_tmout: Duration) -> Result + where + E: Executor + HasObservers + HasInProcessHooks, + EM: EventFirer + EventRestarter, + OF: Feedback, + E::State: State + HasExecutions + HasSolutions + HasCorpus, + Z: HasObjective, + { + let ret; + #[cfg(feature = "std")] + unsafe { + let data = &mut GLOBAL_STATE; + crate::executors::hooks::windows::windows_exception_handler::setup_panic_hook::< + E, + EM, + OF, + Z, + >(); + setup_exception_handler(data)?; + compiler_fence(Ordering::SeqCst); + let crash_handler = + crate::executors::hooks::windows::windows_exception_handler::inproc_crash_handler::< + E, + EM, + OF, + Z, + > as *const _; + let timeout_handler = + crate::executors::hooks::windows::windows_exception_handler::inproc_timeout_handler::< + E, + EM, + OF, + Z, + > as *const c_void; + let timer = TimerStruct::new(exec_tmout, timeout_handler); + ret = Ok(Self { + crash_handler, + timeout_handler, + timer, + }); + } + #[cfg(not(feature = "std"))] + { + ret = Ok(Self {}); + } + + ret + } + + /// Create a new [`InProcessHooks`] + #[cfg(all(not(unix), not(windows)))] + #[allow(unused_variables)] + pub fn new(exec_tmout: Duration) -> Result + where + E: Executor + HasObservers + HasInProcessHooks, + EM: EventFirer + EventRestarter, + OF: Feedback, + E::State: HasExecutions + HasSolutions + HasCorpus, + Z: HasObjective, + { + #[cfg_attr(miri, allow(unused_variables))] + let ret = Self {}; + Ok(ret) + } + + /// Replace the handlers with `nop` handlers, deactivating the handlers + #[must_use] + #[cfg(not(windows))] + pub fn nop() -> Self { + Self { + #[cfg(feature = "std")] + crash_handler: ptr::null(), + #[cfg(feature = "std")] + timeout_handler: ptr::null(), + #[cfg(feature = "std")] + timer: TimerStruct::new(Duration::from_millis(5000)), + } + } +} + +/// The global state of the in-process harness. +#[derive(Debug)] +pub struct InProcessExecutorHandlerData { + /// the pointer to the state + pub state_ptr: *mut c_void, + /// the pointer to the event mgr + pub event_mgr_ptr: *mut c_void, + /// the pointer to the fuzzer + pub fuzzer_ptr: *mut c_void, + /// the pointer to the executor + pub executor_ptr: *const c_void, + pub(crate) current_input_ptr: *const c_void, + pub(crate) in_handler: bool, + + /// The timeout handler + #[cfg(feature = "std")] + pub(crate) crash_handler: *const c_void, + /// The timeout handler + #[cfg(feature = "std")] + pub(crate) timeout_handler: *const c_void, + + #[cfg(all(windows, feature = "std"))] + pub(crate) ptp_timer: Option, + #[cfg(all(windows, feature = "std"))] + pub(crate) in_target: u64, + #[cfg(all(windows, feature = "std"))] + pub(crate) critical: *mut c_void, +} + +unsafe impl Send for InProcessExecutorHandlerData {} +unsafe impl Sync for InProcessExecutorHandlerData {} + +impl InProcessExecutorHandlerData { + #[cfg(any(unix, feature = "std"))] + pub(crate) fn executor_mut<'a, E>(&self) -> &'a mut E { + unsafe { (self.executor_ptr as *mut E).as_mut().unwrap() } + } + + #[cfg(any(unix, feature = "std"))] + pub(crate) fn state_mut<'a, S>(&self) -> &'a mut S { + unsafe { (self.state_ptr as *mut S).as_mut().unwrap() } + } + + #[cfg(any(unix, feature = "std"))] + pub(crate) fn event_mgr_mut<'a, EM>(&self) -> &'a mut EM { + unsafe { (self.event_mgr_ptr as *mut EM).as_mut().unwrap() } + } + + #[cfg(any(unix, feature = "std"))] + pub(crate) fn fuzzer_mut<'a, Z>(&self) -> &'a mut Z { + unsafe { (self.fuzzer_ptr as *mut Z).as_mut().unwrap() } + } + + #[cfg(any(unix, feature = "std"))] + pub(crate) fn take_current_input<'a, I>(&mut self) -> &'a I { + let r = unsafe { (self.current_input_ptr as *const I).as_ref().unwrap() }; + self.current_input_ptr = ptr::null(); + r + } + + #[cfg(any(unix, feature = "std"))] + pub(crate) fn is_valid(&self) -> bool { + !self.current_input_ptr.is_null() + } + + #[cfg(any(unix, feature = "std"))] + pub(crate) fn set_in_handler(&mut self, v: bool) -> bool { + let old = self.in_handler; + self.in_handler = v; + old + } +} + +/// Exception handling needs some nasty unsafe. +pub(crate) static mut GLOBAL_STATE: InProcessExecutorHandlerData = InProcessExecutorHandlerData { + // The state ptr for signal handling + state_ptr: null_mut(), + // The event manager ptr for signal handling + event_mgr_ptr: null_mut(), + // The fuzzer ptr for signal handling + fuzzer_ptr: null_mut(), + // The executor ptr for signal handling + executor_ptr: ptr::null(), + // The current input for signal handling + current_input_ptr: ptr::null(), + + in_handler: false, + + // The crash handler fn + #[cfg(feature = "std")] + crash_handler: ptr::null(), + // The timeout handler fn + #[cfg(feature = "std")] + timeout_handler: ptr::null(), + #[cfg(all(windows, feature = "std"))] + ptp_timer: None, + #[cfg(all(windows, feature = "std"))] + in_target: 0, + #[cfg(all(windows, feature = "std"))] + critical: null_mut(), +}; + +/// Get the inprocess [`crate::state::State`] +#[must_use] +pub fn inprocess_get_state<'a, S>() -> Option<&'a mut S> { + unsafe { (GLOBAL_STATE.state_ptr as *mut S).as_mut() } +} + +/// Get the [`crate::events::EventManager`] +#[must_use] +pub fn inprocess_get_event_manager<'a, EM>() -> Option<&'a mut EM> { + unsafe { (GLOBAL_STATE.event_mgr_ptr as *mut EM).as_mut() } +} + +/// Gets the inprocess [`crate::fuzzer::Fuzzer`] +#[must_use] +pub fn inprocess_get_fuzzer<'a, F>() -> Option<&'a mut F> { + unsafe { (GLOBAL_STATE.fuzzer_ptr as *mut F).as_mut() } +} + +/// Gets the inprocess [`Executor`] +#[must_use] +pub fn inprocess_get_executor<'a, E>() -> Option<&'a mut E> { + unsafe { (GLOBAL_STATE.executor_ptr as *mut E).as_mut() } +} + +/// Gets the inprocess input +#[must_use] +pub fn inprocess_get_input<'a, I>() -> Option<&'a I> { + unsafe { (GLOBAL_STATE.current_input_ptr as *const I).as_ref() } +} + +/// Know if we ar eexecuting in a crash/timeout handler +#[must_use] +pub fn inprocess_in_handler() -> bool { + unsafe { GLOBAL_STATE.in_handler } +} diff --git a/libafl/src/executors/hooks/inprocess_fork.rs b/libafl/src/executors/hooks/inprocess_fork.rs new file mode 100644 index 0000000000..202bff975c --- /dev/null +++ b/libafl/src/executors/hooks/inprocess_fork.rs @@ -0,0 +1,172 @@ +//! The hook for the `InProcessForkExecutor` +use alloc::vec::Vec; +use core::{ + ffi::c_void, + ptr::null, + sync::atomic::{compiler_fence, Ordering}, +}; +use std::intrinsics::transmute; + +#[cfg(not(miri))] +use libafl_bolts::os::unix_signals::setup_signal_handler; +use libafl_bolts::os::unix_signals::{ucontext_t, Handler, Signal}; +use libc::siginfo_t; + +use crate::{ + executors::{ + common_signals, + hooks::ExecutorHook, + inprocess_fork::{child_signal_handlers, ForkHandlerFuncPtr}, + HasObservers, + }, + Error, +}; + +/// The inmem fork executor's hooks. +#[derive(Debug)] +pub struct InChildProcessHooks { + /// On crash C function pointer + pub crash_handler: *const c_void, + /// On timeout C function pointer + pub timeout_handler: *const c_void, +} + +impl ExecutorHook for InChildProcessHooks { + /// Init this hook + fn init(&mut self, _state: &mut S) {} + + /// Call before running a target. + fn pre_exec( + &mut self, + _fuzzer: &mut Z, + _state: &mut S, + _mgr: &mut EM, + _input: &I, + ) { + unsafe { + let data = &mut FORK_EXECUTOR_GLOBAL_DATA; + data.crash_handler = self.crash_handler; + data.timeout_handler = self.timeout_handler; + compiler_fence(Ordering::SeqCst); + } + } + + fn post_exec( + &mut self, + _fuzzer: &mut Z, + _state: &mut S, + _mgr: &mut EM, + _input: &I, + ) { + } +} + +impl InChildProcessHooks { + /// Create new [`InChildProcessHooks`]. + pub fn new() -> Result + where + E: HasObservers, + { + #[cfg_attr(miri, allow(unused_variables))] + unsafe { + let data = &mut FORK_EXECUTOR_GLOBAL_DATA; + // child_signal_handlers::setup_child_panic_hook::(); + #[cfg(not(miri))] + setup_signal_handler(data)?; + compiler_fence(Ordering::SeqCst); + Ok(Self { + crash_handler: child_signal_handlers::child_crash_handler:: as *const c_void, + timeout_handler: child_signal_handlers::child_timeout_handler:: as *const c_void, + }) + } + } + + /// Replace the hooks with `nop` hooks, deactivating the hooks + #[must_use] + pub fn nop() -> Self { + Self { + crash_handler: null(), + timeout_handler: null(), + } + } +} + +/// The global state of the in-process-fork harness. + +#[derive(Debug)] +pub(crate) struct InProcessForkExecutorGlobalData { + /// Stores a pointer to the fork executor struct + pub executor_ptr: *const c_void, + /// Stores a pointer to the state + pub state_ptr: *const c_void, + /// Stores a pointer to the current input + pub current_input_ptr: *const c_void, + /// Stores a pointer to the crash_handler function + pub crash_handler: *const c_void, + /// Stores a pointer to the timeout_handler function + pub timeout_handler: *const c_void, +} + +unsafe impl Sync for InProcessForkExecutorGlobalData {} + +unsafe impl Send for InProcessForkExecutorGlobalData {} + +impl InProcessForkExecutorGlobalData { + pub(crate) fn executor_mut<'a, E>(&self) -> &'a mut E { + unsafe { (self.executor_ptr as *mut E).as_mut().unwrap() } + } + + pub(crate) fn state_mut<'a, S>(&self) -> &'a mut S { + unsafe { (self.state_ptr as *mut S).as_mut().unwrap() } + } + + /*fn current_input<'a, I>(&self) -> &'a I { + unsafe { (self.current_input_ptr as *const I).as_ref().unwrap() } + }*/ + + pub(crate) fn take_current_input<'a, I>(&mut self) -> &'a I { + let r = unsafe { (self.current_input_ptr as *const I).as_ref().unwrap() }; + self.current_input_ptr = null(); + r + } + + pub(crate) fn is_valid(&self) -> bool { + !self.current_input_ptr.is_null() + } +} + +/// a static variable storing the global state + +pub(crate) static mut FORK_EXECUTOR_GLOBAL_DATA: InProcessForkExecutorGlobalData = + InProcessForkExecutorGlobalData { + executor_ptr: null(), + state_ptr: null(), + current_input_ptr: null(), + crash_handler: null(), + timeout_handler: null(), + }; + +impl Handler for InProcessForkExecutorGlobalData { + fn handle(&mut self, signal: Signal, info: &mut siginfo_t, context: Option<&mut ucontext_t>) { + match signal { + Signal::SigUser2 | Signal::SigAlarm => unsafe { + if !FORK_EXECUTOR_GLOBAL_DATA.timeout_handler.is_null() { + let func: ForkHandlerFuncPtr = + transmute(FORK_EXECUTOR_GLOBAL_DATA.timeout_handler); + (func)(signal, info, context, &mut FORK_EXECUTOR_GLOBAL_DATA); + } + }, + _ => unsafe { + if !FORK_EXECUTOR_GLOBAL_DATA.crash_handler.is_null() { + let func: ForkHandlerFuncPtr = + transmute(FORK_EXECUTOR_GLOBAL_DATA.crash_handler); + (func)(signal, info, context, &mut FORK_EXECUTOR_GLOBAL_DATA); + } + }, + } + } + + fn signals(&self) -> Vec { + common_signals() + } +} diff --git a/libafl/src/executors/hooks/mod.rs b/libafl/src/executors/hooks/mod.rs new file mode 100644 index 0000000000..add21cde11 --- /dev/null +++ b/libafl/src/executors/hooks/mod.rs @@ -0,0 +1,102 @@ +//! Hooks for the executors. +//! These will be executed right before and after the executor's harness run. + +use crate::executors::HasObservers; + +/// windows crash/timeout handler and asan death callback +#[cfg(windows)] +pub mod windows; + +/// *nix crash handler +#[cfg(all(unix, feature = "std"))] +pub mod unix; + +#[cfg(all(feature = "std", unix))] +/// The hook for inprocess fork executor +pub mod inprocess_fork; + +/// The hook for inprocess executor +pub mod inprocess; + +/// Timer-related stuff +#[cfg(feature = "std")] +pub mod timer; + +/// The hook that runs before and after the executor runs the target +pub trait ExecutorHook { + /// Init this hook + fn init(&mut self, state: &mut S); + /// The hook that runs before runs the target + fn pre_exec(&mut self, fuzzer: &mut Z, state: &mut S, mgr: &mut EM, input: &I); + /// The hook that runs before runs the target + fn post_exec(&mut self, fuzzer: &mut Z, state: &mut S, mgr: &mut EM, input: &I); +} + +/// The hook that runs before and after the executor runs the target +pub trait ExecutorHooksTuple { + /// Init these hooks + fn init_all(&mut self, state: &mut S); + /// The hooks that runs before runs the target + fn pre_exec_all(&mut self, fuzzer: &mut Z, state: &mut S, mgr: &mut EM, input: &I); + /// The hooks that runs after runs the target + fn post_exec_all( + &mut self, + fuzzer: &mut Z, + state: &mut S, + mgr: &mut EM, + input: &I, + ); +} + +impl ExecutorHooksTuple for () { + fn init_all(&mut self, _state: &mut S) {} + fn pre_exec_all( + &mut self, + _fuzzer: &mut Z, + _state: &mut S, + _mgr: &mut EM, + _input: &I, + ) { + } + fn post_exec_all( + &mut self, + _fuzzer: &mut Z, + _state: &mut S, + _mgr: &mut EM, + _input: &I, + ) { + } +} + +impl ExecutorHooksTuple for (Head, Tail) +where + Head: ExecutorHook, + Tail: ExecutorHooksTuple, +{ + fn init_all(&mut self, state: &mut S) { + self.0.init::(state); + self.1.init_all::(state); + } + + fn pre_exec_all( + &mut self, + fuzzer: &mut Z, + state: &mut S, + mgr: &mut EM, + input: &I, + ) { + self.0.pre_exec(fuzzer, state, mgr, input); + self.1.pre_exec_all(fuzzer, state, mgr, input); + } + + fn post_exec_all( + &mut self, + fuzzer: &mut Z, + state: &mut S, + mgr: &mut EM, + input: &I, + ) { + self.0.post_exec(fuzzer, state, mgr, input); + self.1.post_exec_all(fuzzer, state, mgr, input); + } +} diff --git a/libafl/src/executors/hooks/timer.rs b/libafl/src/executors/hooks/timer.rs new file mode 100644 index 0000000000..c35722b492 --- /dev/null +++ b/libafl/src/executors/hooks/timer.rs @@ -0,0 +1,378 @@ +//! The struct `TimerStruct` will absorb all the difference in timeout implementation in various system. +use core::time::Duration; +#[cfg(any(windows, target_os = "linux"))] +use core::{ + ffi::c_void, + ptr::{addr_of_mut, write_volatile}, +}; +#[cfg(target_os = "linux")] +use core::{ + mem::zeroed, + ptr::{addr_of, null_mut}, +}; + +#[cfg(all(unix, not(target_os = "linux")))] +pub const ITIMER_REAL: core::ffi::c_int = 0; + +#[cfg(windows)] +use core::sync::atomic::{compiler_fence, Ordering}; + +#[cfg(target_os = "linux")] +use libafl_bolts::current_time; +#[cfg(windows)] +use windows::Win32::{ + Foundation::FILETIME, + System::Threading::{ + CreateThreadpoolTimer, EnterCriticalSection, InitializeCriticalSection, + LeaveCriticalSection, SetThreadpoolTimer, CRITICAL_SECTION, PTP_CALLBACK_INSTANCE, + PTP_TIMER, TP_CALLBACK_ENVIRON_V3, + }, +}; + +#[cfg(any(windows, target_os = "linux"))] +use crate::executors::hooks::inprocess::GLOBAL_STATE; + +#[repr(C)] +#[cfg(all(unix, not(target_os = "linux")))] +#[derive(Copy, Clone)] +pub(crate) struct Timeval { + pub tv_sec: i64, + pub tv_usec: i64, +} + +#[cfg(all(unix, not(target_os = "linux")))] +impl core::fmt::Debug for Timeval { + #[allow(clippy::cast_sign_loss)] + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!( + f, + "Timeval {{ tv_sec: {:?}, tv_usec: {:?} (tv: {:?}) }}", + self.tv_sec, + self.tv_usec, + Duration::new(self.tv_sec as _, (self.tv_usec * 1000) as _) + ) + } +} + +#[repr(C)] +#[cfg(all(unix, not(target_os = "linux")))] +#[derive(Debug, Copy, Clone)] +pub(crate) struct Itimerval { + pub it_interval: Timeval, + pub it_value: Timeval, +} + +#[cfg(all(feature = "std", unix, not(target_os = "linux")))] +extern "C" { + pub fn setitimer( + which: libc::c_int, + new_value: *mut Itimerval, + old_value: *mut Itimerval, + ) -> libc::c_int; +} + +/// The strcut about all the internals of the timer. +/// This struct absorb all platform specific differences about timer. +#[allow(missing_debug_implementations)] +pub struct TimerStruct { + // timeout time (windows) + #[cfg(windows)] + milli_sec: i64, + #[cfg(windows)] + ptp_timer: PTP_TIMER, + #[cfg(windows)] + critical: CRITICAL_SECTION, + #[cfg(target_os = "linux")] + pub(crate) batch_mode: bool, + #[cfg(target_os = "linux")] + pub(crate) exec_tmout: Duration, + #[cfg(all(unix, not(target_os = "linux")))] + itimerval: Itimerval, + #[cfg(target_os = "linux")] + pub(crate) timerid: libc::timer_t, + #[cfg(target_os = "linux")] + pub(crate) itimerspec: libc::itimerspec, + #[cfg(target_os = "linux")] + pub(crate) executions: u32, + #[cfg(target_os = "linux")] + pub(crate) avg_mul_k: u32, + #[cfg(target_os = "linux")] + pub(crate) last_signal_time: Duration, + #[cfg(target_os = "linux")] + pub(crate) avg_exec_time: Duration, + #[cfg(target_os = "linux")] + pub(crate) start_time: Duration, + #[cfg(target_os = "linux")] + pub(crate) tmout_start_time: Duration, +} + +#[cfg(all(feature = "std", windows))] +#[allow(non_camel_case_types)] +type PTP_TIMER_CALLBACK = unsafe extern "system" fn( + param0: PTP_CALLBACK_INSTANCE, + param1: *mut c_void, + param2: PTP_TIMER, +); + +impl TimerStruct { + /// Timeout value in milli seconds + #[cfg(windows)] + #[must_use] + pub fn milli_sec(&self) -> i64 { + self.milli_sec + } + + #[cfg(windows)] + /// Timeout value in milli seconds (mut ref) + pub fn milli_sec_mut(&mut self) -> &mut i64 { + &mut self.milli_sec + } + + /// The timer object for windows + #[cfg(windows)] + #[must_use] + pub fn ptp_timer(&self) -> &PTP_TIMER { + &self.ptp_timer + } + + #[cfg(windows)] + /// The timer object for windows + pub fn ptp_timer_mut(&mut self) -> &mut PTP_TIMER { + &mut self.ptp_timer + } + + /// The critical section, we need to use critical section to access the globals + #[cfg(windows)] + #[must_use] + pub fn critical(&self) -> &CRITICAL_SECTION { + &self.critical + } + + #[cfg(windows)] + /// The critical section (mut ref), we need to use critical section to access the globals + pub fn critical_mut(&mut self) -> &mut CRITICAL_SECTION { + &mut self.critical + } + + /// Create a `TimerStruct` with the specified timeout + #[cfg(all(unix, not(target_os = "linux")))] + pub fn new(exec_tmout: Duration) -> Self { + let milli_sec = exec_tmout.as_millis(); + let it_value = Timeval { + tv_sec: (milli_sec / 1000) as i64, + tv_usec: (milli_sec % 1000) as i64, + }; + let it_interval = Timeval { + tv_sec: 0, + tv_usec: 0, + }; + let itimerval = Itimerval { + it_interval, + it_value, + }; + Self { itimerval } + } + + /// Constructor + /// # Safety + /// This function calls transmute to setup the timeout handler for windows + #[cfg(windows)] + #[must_use] + pub unsafe fn new(exec_tmout: Duration, timeout_handler: *const c_void) -> Self { + let milli_sec = exec_tmout.as_millis() as i64; + + let timeout_handler: PTP_TIMER_CALLBACK = unsafe { std::mem::transmute(timeout_handler) }; + let ptp_timer = unsafe { + CreateThreadpoolTimer( + Some(timeout_handler), + Some(addr_of_mut!(GLOBAL_STATE) as *mut c_void), + Some(&TP_CALLBACK_ENVIRON_V3::default()), + ) + } + .expect("CreateThreadpoolTimer failed!"); + + let mut critical = CRITICAL_SECTION::default(); + unsafe { + InitializeCriticalSection(&mut critical); + } + Self { + milli_sec, + ptp_timer, + critical, + } + } + + #[cfg(target_os = "linux")] + #[must_use] + #[allow(unused_unsafe)] + #[allow(unused_mut)] + /// Create a `TimerStruct` with the specified timeout + pub fn new(exec_tmout: Duration) -> Self { + let milli_sec = exec_tmout.as_millis(); + let it_value = libc::timespec { + tv_sec: (milli_sec / 1000) as _, + tv_nsec: ((milli_sec % 1000) * 1000 * 1000) as _, + }; + let it_interval = libc::timespec { + tv_sec: 0, + tv_nsec: 0, + }; + let itimerspec = libc::itimerspec { + it_interval, + it_value, + }; + let mut timerid: libc::timer_t = null_mut(); + unsafe { + #[cfg(not(miri))] + // creates a new per-process interval timer + libc::timer_create(libc::CLOCK_MONOTONIC, null_mut(), addr_of_mut!(timerid)); + } + + Self { + batch_mode: false, + itimerspec, + timerid, + exec_tmout, + executions: 0, + avg_mul_k: 1, + last_signal_time: Duration::ZERO, + avg_exec_time: Duration::ZERO, + start_time: Duration::ZERO, + tmout_start_time: Duration::ZERO, + } + } + + #[cfg(target_os = "linux")] + #[must_use] + /// Constructor but use batch mode + pub fn batch_mode(exec_tmout: Duration) -> Self { + let mut me = Self::new(exec_tmout); + me.batch_mode = true; + me + } + + #[cfg(all(unix, not(target_os = "linux")))] + /// Set up timer + pub fn set_timer(&mut self) { + unsafe { + setitimer(ITIMER_REAL, &mut self.itimerval, core::ptr::null_mut()); + } + } + + #[cfg(windows)] + #[allow(clippy::cast_sign_loss)] + /// Set timer + pub fn set_timer(&mut self) { + unsafe { + let data = &mut GLOBAL_STATE; + + write_volatile(&mut data.ptp_timer, Some(*self.ptp_timer())); + write_volatile( + &mut data.critical, + addr_of_mut!(*self.critical_mut()) as *mut c_void, + ); + let tm: i64 = -self.milli_sec() * 10 * 1000; + let ft = FILETIME { + dwLowDateTime: (tm & 0xffffffff) as u32, + dwHighDateTime: (tm >> 32) as u32, + }; + + // enter critical section then set timer + compiler_fence(Ordering::SeqCst); + EnterCriticalSection(self.critical_mut()); + compiler_fence(Ordering::SeqCst); + data.in_target = 1; + compiler_fence(Ordering::SeqCst); + LeaveCriticalSection(self.critical_mut()); + compiler_fence(Ordering::SeqCst); + + SetThreadpoolTimer(*self.ptp_timer(), Some(&ft), 0, 0); + } + } + + /// Set up timer + #[cfg(target_os = "linux")] + pub fn set_timer(&mut self) { + unsafe { + if self.batch_mode { + let data = &mut GLOBAL_STATE; + write_volatile(&mut data.executor_ptr, self as *mut _ as *mut c_void); + + if self.executions == 0 { + libc::timer_settime(self.timerid, 0, addr_of_mut!(self.itimerspec), null_mut()); + self.tmout_start_time = current_time(); + } + self.start_time = current_time(); + } else { + #[cfg(not(miri))] + libc::timer_settime(self.timerid, 0, addr_of_mut!(self.itimerspec), null_mut()); + } + } + } + + #[cfg(all(unix, not(target_os = "linux")))] + /// Disalarm timer + pub fn unset_timer(&mut self) { + unsafe { + let mut itimerval_zero: Itimerval = core::mem::zeroed(); + setitimer(ITIMER_REAL, &mut itimerval_zero, core::ptr::null_mut()); + } + } + + /// Disalarm timer + #[cfg(target_os = "linux")] + #[allow(unused_variables)] + pub fn unset_timer(&mut self) { + if self.batch_mode { + unsafe { + let elapsed = current_time() - self.tmout_start_time; + // elapsed may be > than tmout in case of received but ingored signal + if elapsed > self.exec_tmout + || self.exec_tmout - elapsed < self.avg_exec_time * self.avg_mul_k + { + let disarmed: libc::itimerspec = zeroed(); + libc::timer_settime(self.timerid, 0, addr_of!(disarmed), null_mut()); + // set timer the next exec + if self.executions > 0 { + self.avg_exec_time = elapsed / self.executions; + self.executions = 0; + } + // readjust K + if self.last_signal_time > self.exec_tmout * self.avg_mul_k + && self.avg_mul_k > 1 + { + self.avg_mul_k -= 1; + } + } else { + self.executions += 1; + } + } + } else { + unsafe { + let disarmed: libc::itimerspec = zeroed(); + #[cfg(not(miri))] + libc::timer_settime(self.timerid, 0, addr_of!(disarmed), null_mut()); + } + } + } + + #[cfg(windows)] + /// Disalarm + pub fn unset_timer(&mut self) { + unsafe { + let data = &mut GLOBAL_STATE; + + compiler_fence(Ordering::SeqCst); + EnterCriticalSection(self.critical_mut()); + compiler_fence(Ordering::SeqCst); + // Timeout handler will do nothing after we increment in_target value. + data.in_target = 0; + compiler_fence(Ordering::SeqCst); + LeaveCriticalSection(self.critical_mut()); + compiler_fence(Ordering::SeqCst); + + // previously this wa post_run_reset + SetThreadpoolTimer(*self.ptp_timer(), None, 0, 0); + } + } +} diff --git a/libafl/src/executors/hooks/unix.rs b/libafl/src/executors/hooks/unix.rs new file mode 100644 index 0000000000..17be77cc50 --- /dev/null +++ b/libafl/src/executors/hooks/unix.rs @@ -0,0 +1,272 @@ +/// The inprocess executor singal handling code for unix +#[cfg(unix)] +pub mod unix_signal_handler { + use alloc::{boxed::Box, string::String, vec::Vec}; + use core::mem::transmute; + use std::{io::Write, panic}; + + use libafl_bolts::os::unix_signals::{ucontext_t, Handler, Signal}; + use libc::siginfo_t; + + use crate::{ + events::{EventFirer, EventRestarter}, + executors::{ + common_signals, + hooks::inprocess::{HasTimeout, InProcessExecutorHandlerData, GLOBAL_STATE}, + inprocess::{run_observers_and_save_state, HasInProcessHooks}, + Executor, ExitKind, HasObservers, + }, + feedbacks::Feedback, + fuzzer::HasObjective, + inputs::{Input, UsesInput}, + state::{HasCorpus, HasExecutions, HasSolutions}, + }; + + pub(crate) type HandlerFuncPtr = unsafe fn( + Signal, + &mut siginfo_t, + Option<&mut ucontext_t>, + data: &mut InProcessExecutorHandlerData, + ); + + /// A handler that does nothing. + /*pub fn nop_handler( + _signal: Signal, + _info: &mut siginfo_t, + _context: Option<&mut ucontext_t>, + _data: &mut InProcessExecutorHandlerData, + ) { + }*/ + + #[cfg(unix)] + impl Handler for InProcessExecutorHandlerData { + fn handle( + &mut self, + signal: Signal, + info: &mut siginfo_t, + context: Option<&mut ucontext_t>, + ) { + unsafe { + let data = &mut GLOBAL_STATE; + let in_handler = data.set_in_handler(true); + match signal { + Signal::SigUser2 | Signal::SigAlarm => { + if !data.timeout_handler.is_null() { + let func: HandlerFuncPtr = transmute(data.timeout_handler); + (func)(signal, info, context, data); + } + } + _ => { + if !data.crash_handler.is_null() { + let func: HandlerFuncPtr = transmute(data.crash_handler); + (func)(signal, info, context, data); + } + } + } + data.set_in_handler(in_handler); + } + } + + fn signals(&self) -> Vec { + common_signals() + } + } + + /// invokes the `post_exec` hook on all observer in case of panic + pub fn setup_panic_hook() + where + E: HasObservers, + EM: EventFirer + EventRestarter, + OF: Feedback, + E::State: HasExecutions + HasSolutions + HasCorpus, + Z: HasObjective, + { + let old_hook = panic::take_hook(); + panic::set_hook(Box::new(move |panic_info| { + old_hook(panic_info); + let data = unsafe { &mut GLOBAL_STATE }; + let in_handler = data.set_in_handler(true); + if data.is_valid() { + // We are fuzzing! + let executor = data.executor_mut::(); + let state = data.state_mut::(); + let input = data.take_current_input::<::Input>(); + let fuzzer = data.fuzzer_mut::(); + let event_mgr = data.event_mgr_mut::(); + + run_observers_and_save_state::( + executor, + state, + input, + fuzzer, + event_mgr, + ExitKind::Crash, + ); + + unsafe { + libc::_exit(128 + 6); + } // SIGABRT exit code + } + data.set_in_handler(in_handler); + })); + } + + /// Timeout-Handler for in-process fuzzing. + /// It will store the current State to shmem, then exit. + /// + /// # Safety + /// Well, signal handling is not safe + #[cfg(unix)] + #[allow(clippy::needless_pass_by_value)] + pub unsafe fn inproc_timeout_handler( + _signal: Signal, + _info: &mut siginfo_t, + _context: Option<&mut ucontext_t>, + data: &mut InProcessExecutorHandlerData, + ) where + E: HasObservers + HasInProcessHooks, + EM: EventFirer + EventRestarter, + OF: Feedback, + E::State: HasExecutions + HasSolutions + HasCorpus, + Z: HasObjective, + { + // this stuff is for batch timeout + if !data.executor_ptr.is_null() + && data + .executor_mut::() + .inprocess_hooks_mut() + .handle_timeout(data) + { + return; + } + + if !data.is_valid() { + log::warn!("TIMEOUT or SIGUSR2 happened, but currently not fuzzing."); + return; + } + + let executor = data.executor_mut::(); + let state = data.state_mut::(); + let event_mgr = data.event_mgr_mut::(); + let fuzzer = data.fuzzer_mut::(); + let input = data.take_current_input::<::Input>(); + + log::error!("Timeout in fuzz run."); + + run_observers_and_save_state::( + executor, + state, + input, + fuzzer, + event_mgr, + ExitKind::Timeout, + ); + log::info!("Exiting"); + libc::_exit(55); + } + + /// Crash-Handler for in-process fuzzing. + /// Will be used for signal handling. + /// It will store the current State to shmem, then exit. + /// + /// # Safety + /// Well, signal handling is not safe + #[allow(clippy::too_many_lines)] + #[allow(clippy::needless_pass_by_value)] + pub unsafe fn inproc_crash_handler( + signal: Signal, + _info: &mut siginfo_t, + _context: Option<&mut ucontext_t>, + data: &mut InProcessExecutorHandlerData, + ) where + E: Executor + HasObservers, + EM: EventFirer + EventRestarter, + OF: Feedback, + E::State: HasExecutions + HasSolutions + HasCorpus, + Z: HasObjective, + { + #[cfg(all(target_os = "android", target_arch = "aarch64"))] + let _context = _context.map(|p| { + &mut *(((p as *mut _ as *mut libc::c_void as usize) + 128) as *mut libc::c_void + as *mut ucontext_t) + }); + + log::error!("Crashed with {signal}"); + if data.is_valid() { + let executor = data.executor_mut::(); + // disarms timeout in case of timeout + let state = data.state_mut::(); + let event_mgr = data.event_mgr_mut::(); + let fuzzer = data.fuzzer_mut::(); + let input = data.take_current_input::<::Input>(); + + log::error!("Child crashed!"); + + { + let mut bsod = Vec::new(); + { + let mut writer = std::io::BufWriter::new(&mut bsod); + writeln!(writer, "input: {:?}", input.generate_name(0)).unwrap(); + libafl_bolts::minibsod::generate_minibsod( + &mut writer, + signal, + _info, + _context.as_deref(), + ) + .unwrap(); + writer.flush().unwrap(); + } + log::error!("{}", std::str::from_utf8(&bsod).unwrap()); + } + + run_observers_and_save_state::( + executor, + state, + input, + fuzzer, + event_mgr, + ExitKind::Crash, + ); + } else { + { + log::error!("Double crash\n"); + #[cfg(target_os = "android")] + let si_addr = (_info._pad[0] as i64) | ((_info._pad[1] as i64) << 32); + #[cfg(not(target_os = "android"))] + let si_addr = { _info.si_addr() as usize }; + + log::error!( + "We crashed at addr 0x{si_addr:x}, but are not in the target... Bug in the fuzzer? Exiting." + ); + + { + let mut bsod = Vec::new(); + { + let mut writer = std::io::BufWriter::new(&mut bsod); + libafl_bolts::minibsod::generate_minibsod( + &mut writer, + signal, + _info, + _context.as_deref(), + ) + .unwrap(); + writer.flush().unwrap(); + } + log::error!("{}", std::str::from_utf8(&bsod).unwrap()); + } + } + + { + log::error!("Type QUIT to restart the child"); + let mut line = String::new(); + while line.trim() != "QUIT" { + std::io::stdin().read_line(&mut line).unwrap(); + } + } + + // TODO tell the parent to not restart + } + + libc::_exit(128 + (signal as i32)); + } +} diff --git a/libafl/src/executors/hooks/windows.rs b/libafl/src/executors/hooks/windows.rs new file mode 100644 index 0000000000..47b6ff181a --- /dev/null +++ b/libafl/src/executors/hooks/windows.rs @@ -0,0 +1,418 @@ +/// Same as `inproc_crash_handler`, but this is called when address sanitizer exits, not from the exception handler +#[cfg(all(windows, feature = "std"))] +pub mod windows_asan_handler { + use alloc::string::String; + use core::sync::atomic::{compiler_fence, Ordering}; + + use windows::Win32::System::Threading::{ + EnterCriticalSection, LeaveCriticalSection, CRITICAL_SECTION, + }; + + use crate::{ + events::{EventFirer, EventRestarter}, + executors::{ + hooks::inprocess::GLOBAL_STATE, inprocess::run_observers_and_save_state, Executor, + ExitKind, HasObservers, + }, + feedbacks::Feedback, + fuzzer::HasObjective, + inputs::UsesInput, + state::{HasCorpus, HasExecutions, HasSolutions}, + }; + + /// # Safety + /// ASAN deatch handler + pub unsafe extern "C" fn asan_death_handler() + where + E: Executor + HasObservers, + EM: EventFirer + EventRestarter, + OF: Feedback, + E::State: HasExecutions + HasSolutions + HasCorpus, + Z: HasObjective, + { + let data = &mut GLOBAL_STATE; + data.set_in_handler(true); + // Have we set a timer_before? + if data.ptp_timer.is_some() { + /* + We want to prevent the timeout handler being run while the main thread is executing the crash handler + Timeout handler runs if it has access to the critical section or data.in_target == 0 + Writing 0 to the data.in_target makes the timeout handler makes the timeout handler invalid. + */ + compiler_fence(Ordering::SeqCst); + EnterCriticalSection(data.critical as *mut CRITICAL_SECTION); + compiler_fence(Ordering::SeqCst); + data.in_target = 0; + compiler_fence(Ordering::SeqCst); + LeaveCriticalSection(data.critical as *mut CRITICAL_SECTION); + compiler_fence(Ordering::SeqCst); + } + + log::error!("ASAN detected crash!"); + if data.current_input_ptr.is_null() { + { + log::error!("Double crash\n"); + log::error!( + "ASAN detected crash but we're not in the target... Bug in the fuzzer? Exiting.", + ); + } + #[cfg(feature = "std")] + { + log::error!("Type QUIT to restart the child"); + let mut line = String::new(); + while line.trim() != "QUIT" { + std::io::stdin().read_line(&mut line).unwrap(); + } + } + + // TODO tell the parent to not restart + } else { + let executor = data.executor_mut::(); + // reset timer + if data.ptp_timer.is_some() { + data.ptp_timer = None; + } + + let state = data.state_mut::(); + let fuzzer = data.fuzzer_mut::(); + let event_mgr = data.event_mgr_mut::(); + + log::error!("Child crashed!"); + + // Make sure we don't crash in the crash handler forever. + let input = data.take_current_input::<::Input>(); + + run_observers_and_save_state::( + executor, + state, + input, + fuzzer, + event_mgr, + ExitKind::Crash, + ); + } + // Don't need to exit, Asan will exit for us + // ExitProcess(1); + } +} + +#[cfg(all(windows, feature = "std"))] +/// The module to take care of windows crash or timeouts +pub mod windows_exception_handler { + #[cfg(feature = "std")] + use alloc::boxed::Box; + use alloc::{string::String, vec::Vec}; + use core::{ + ffi::c_void, + mem::transmute, + ptr, + sync::atomic::{compiler_fence, Ordering}, + }; + #[cfg(feature = "std")] + use std::panic; + + use libafl_bolts::os::windows_exceptions::{ + ExceptionCode, Handler, CRASH_EXCEPTIONS, EXCEPTION_HANDLERS_SIZE, EXCEPTION_POINTERS, + }; + use windows::Win32::System::Threading::{ + EnterCriticalSection, ExitProcess, LeaveCriticalSection, CRITICAL_SECTION, + }; + + use crate::{ + events::{EventFirer, EventRestarter}, + executors::{ + hooks::inprocess::{HasTimeout, InProcessExecutorHandlerData, GLOBAL_STATE}, + inprocess::{run_observers_and_save_state, HasInProcessHooks}, + Executor, ExitKind, HasObservers, + }, + feedbacks::Feedback, + fuzzer::HasObjective, + inputs::UsesInput, + state::{HasCorpus, HasExecutions, HasSolutions, State}, + }; + + pub(crate) type HandlerFuncPtr = + unsafe fn(*mut EXCEPTION_POINTERS, &mut InProcessExecutorHandlerData); + + /*pub unsafe fn nop_handler( + _code: ExceptionCode, + _exception_pointers: *mut EXCEPTION_POINTERS, + _data: &mut InProcessExecutorHandlerData, + ) { + }*/ + + impl Handler for InProcessExecutorHandlerData { + #[allow(clippy::not_unsafe_ptr_arg_deref)] + fn handle(&mut self, _code: ExceptionCode, exception_pointers: *mut EXCEPTION_POINTERS) { + unsafe { + let data = &mut GLOBAL_STATE; + let in_handler = data.set_in_handler(true); + if !data.crash_handler.is_null() { + let func: HandlerFuncPtr = transmute(data.crash_handler); + (func)(exception_pointers, data); + } + data.set_in_handler(in_handler); + } + } + + fn exceptions(&self) -> Vec { + let crash_list = CRASH_EXCEPTIONS.to_vec(); + assert!(crash_list.len() < EXCEPTION_HANDLERS_SIZE - 1); + crash_list + } + } + + /// invokes the `post_exec` hook on all observer in case of panic + /// + /// # Safety + /// Well, exception handling is not safe + #[cfg(feature = "std")] + pub fn setup_panic_hook() + where + E: HasObservers, + EM: EventFirer + EventRestarter, + OF: Feedback, + E::State: HasExecutions + HasSolutions + HasCorpus, + Z: HasObjective, + { + let old_hook = panic::take_hook(); + panic::set_hook(Box::new(move |panic_info| { + let data = unsafe { &mut GLOBAL_STATE }; + let in_handler = data.set_in_handler(true); + // Have we set a timer_before? + unsafe { + if data.ptp_timer.is_some() { + /* + We want to prevent the timeout handler being run while the main thread is executing the crash handler + Timeout handler runs if it has access to the critical section or data.in_target == 0 + Writing 0 to the data.in_target makes the timeout handler makes the timeout handler invalid. + */ + compiler_fence(Ordering::SeqCst); + EnterCriticalSection(data.critical as *mut CRITICAL_SECTION); + compiler_fence(Ordering::SeqCst); + data.in_target = 0; + compiler_fence(Ordering::SeqCst); + LeaveCriticalSection(data.critical as *mut CRITICAL_SECTION); + compiler_fence(Ordering::SeqCst); + } + } + + if data.is_valid() { + // We are fuzzing! + let executor = data.executor_mut::(); + let state = data.state_mut::(); + let fuzzer = data.fuzzer_mut::(); + let event_mgr = data.event_mgr_mut::(); + + let input = data.take_current_input::<::Input>(); + + run_observers_and_save_state::( + executor, + state, + input, + fuzzer, + event_mgr, + ExitKind::Crash, + ); + + unsafe { + ExitProcess(1); + } + } + old_hook(panic_info); + data.set_in_handler(in_handler); + })); + } + + /// Timeout handler for windows + /// + /// # Safety + /// Well, exception handling is not safe + pub unsafe extern "system" fn inproc_timeout_handler( + _p0: *mut u8, + global_state: *mut c_void, + _p1: *mut u8, + ) where + E: HasObservers + HasInProcessHooks, + EM: EventFirer + EventRestarter, + OF: Feedback, + E::State: State + HasExecutions + HasSolutions + HasCorpus, + Z: HasObjective, + { + let data: &mut InProcessExecutorHandlerData = + &mut *(global_state as *mut InProcessExecutorHandlerData); + compiler_fence(Ordering::SeqCst); + EnterCriticalSection((data.critical as *mut CRITICAL_SECTION).as_mut().unwrap()); + compiler_fence(Ordering::SeqCst); + + if !data.executor_ptr.is_null() + && data + .executor_mut::() + .inprocess_hooks_mut() + .handle_timeout() + { + compiler_fence(Ordering::SeqCst); + LeaveCriticalSection((data.critical as *mut CRITICAL_SECTION).as_mut().unwrap()); + compiler_fence(Ordering::SeqCst); + + return; + } + + if data.in_target == 1 { + let executor = data.executor_mut::(); + let state = data.state_mut::(); + let fuzzer = data.fuzzer_mut::(); + let event_mgr = data.event_mgr_mut::(); + + if data.current_input_ptr.is_null() { + log::error!("TIMEOUT or SIGUSR2 happened, but currently not fuzzing. Exiting"); + } else { + log::error!("Timeout in fuzz run."); + + let input = (data.current_input_ptr as *const ::Input) + .as_ref() + .unwrap(); + data.current_input_ptr = ptr::null_mut(); + + run_observers_and_save_state::( + executor, + state, + input, + fuzzer, + event_mgr, + ExitKind::Timeout, + ); + + compiler_fence(Ordering::SeqCst); + + ExitProcess(1); + } + } + compiler_fence(Ordering::SeqCst); + LeaveCriticalSection((data.critical as *mut CRITICAL_SECTION).as_mut().unwrap()); + compiler_fence(Ordering::SeqCst); + // log::info!("TIMER INVOKED!"); + } + + /// Crash handler for windows + /// + /// # Safety + /// Well, exception handling is not safe + #[allow(clippy::too_many_lines)] + pub unsafe fn inproc_crash_handler( + exception_pointers: *mut EXCEPTION_POINTERS, + data: &mut InProcessExecutorHandlerData, + ) where + E: Executor + HasObservers, + EM: EventFirer + EventRestarter, + OF: Feedback, + E::State: HasExecutions + HasSolutions + HasCorpus, + Z: HasObjective, + { + // Have we set a timer_before? + if data.ptp_timer.is_some() { + /* + We want to prevent the timeout handler being run while the main thread is executing the crash handler + Timeout handler runs if it has access to the critical section or data.in_target == 0 + Writing 0 to the data.in_target makes the timeout handler makes the timeout handler invalid. + */ + compiler_fence(Ordering::SeqCst); + EnterCriticalSection(data.critical as *mut CRITICAL_SECTION); + compiler_fence(Ordering::SeqCst); + data.in_target = 0; + compiler_fence(Ordering::SeqCst); + LeaveCriticalSection(data.critical as *mut CRITICAL_SECTION); + compiler_fence(Ordering::SeqCst); + } + + // Is this really crash? + let mut is_crash = true; + #[cfg(feature = "std")] + if let Some(exception_pointers) = exception_pointers.as_mut() { + let code = ExceptionCode::try_from( + exception_pointers + .ExceptionRecord + .as_mut() + .unwrap() + .ExceptionCode + .0, + ) + .unwrap(); + + let exception_list = data.exceptions(); + if exception_list.contains(&code) { + log::error!("Crashed with {code}"); + } else { + // log::trace!("Exception code received, but {code} is not in CRASH_EXCEPTIONS"); + is_crash = false; + } + } else { + log::error!("Crashed without exception (probably due to SIGABRT)"); + }; + + if data.current_input_ptr.is_null() { + { + log::error!("Double crash\n"); + let crash_addr = exception_pointers + .as_mut() + .unwrap() + .ExceptionRecord + .as_mut() + .unwrap() + .ExceptionAddress as usize; + + log::error!( + "We crashed at addr 0x{crash_addr:x}, but are not in the target... Bug in the fuzzer? Exiting." + ); + } + #[cfg(feature = "std")] + { + log::error!("Type QUIT to restart the child"); + let mut line = String::new(); + while line.trim() != "QUIT" { + std::io::stdin().read_line(&mut line).unwrap(); + } + } + + // TODO tell the parent to not restart + } else { + let executor = data.executor_mut::(); + // reset timer + if data.ptp_timer.is_some() { + data.ptp_timer = None; + } + + let state = data.state_mut::(); + let fuzzer = data.fuzzer_mut::(); + let event_mgr = data.event_mgr_mut::(); + + if is_crash { + log::error!("Child crashed!"); + } else { + // log::info!("Exception received!"); + } + + // Make sure we don't crash in the crash handler forever. + if is_crash { + let input = data.take_current_input::<::Input>(); + + run_observers_and_save_state::( + executor, + state, + input, + fuzzer, + event_mgr, + ExitKind::Crash, + ); + } else { + // This is not worth saving + } + } + + if is_crash { + log::info!("Exiting!"); + ExitProcess(1); + } + // log::info!("Not Exiting!"); + } +} diff --git a/libafl/src/executors/inprocess.rs b/libafl/src/executors/inprocess.rs index 599ac72bc1..5fd8c19524 100644 --- a/libafl/src/executors/inprocess.rs +++ b/libafl/src/executors/inprocess.rs @@ -5,77 +5,61 @@ #![allow(clippy::needless_pass_by_value)] use alloc::boxed::Box; -#[cfg(unix)] -use alloc::vec::Vec; -#[cfg(all(feature = "std", unix, target_os = "linux"))] -use core::ptr::addr_of_mut; -#[cfg(all(unix, feature = "std"))] -use core::time::Duration; use core::{ borrow::BorrowMut, ffi::c_void, fmt::{self, Debug, Formatter}, marker::PhantomData, - ptr::{self, null_mut}, -}; -#[cfg(any(unix, all(windows, feature = "std")))] -use core::{ - ptr::write_volatile, + ptr::{null, write_volatile}, sync::atomic::{compiler_fence, Ordering}, + time::Duration, }; -#[cfg(all(feature = "std", unix))] -use std::intrinsics::transmute; -#[cfg(all(unix, not(miri)))] -use libafl_bolts::os::unix_signals::setup_signal_handler; -#[cfg(unix)] -use libafl_bolts::os::unix_signals::Signal; -#[cfg(all(feature = "std", unix))] -use libafl_bolts::os::unix_signals::{ucontext_t, Handler}; -#[cfg(all(windows, feature = "std"))] -use libafl_bolts::os::windows_exceptions::setup_exception_handler; -#[cfg(all(feature = "std", unix))] -use libafl_bolts::shmem::ShMemProvider; -#[cfg(all(feature = "std", unix))] -use libc::siginfo_t; -#[cfg(all(feature = "std", unix))] -use nix::{ - sys::wait::{waitpid, WaitStatus}, - unistd::{fork, ForkResult}, -}; +use libafl_bolts::tuples::{tuple_list, Merge}; #[cfg(windows)] use windows::Win32::System::Threading::SetThreadStackGuarantee; -#[cfg(all(windows, feature = "std"))] -use windows::Win32::System::Threading::PTP_TIMER; +#[cfg(all(feature = "std", target_os = "linux"))] +use crate::executors::hooks::inprocess::HasTimeout; +#[cfg(all(windows, feature = "std"))] +use crate::executors::hooks::inprocess::HasTimeout; use crate::{ - events::{EventFirer, EventRestarter}, - executors::{Executor, ExitKind, HasObservers}, + corpus::{Corpus, Testcase}, + events::{Event, EventFirer, EventRestarter}, + executors::{ + hooks::{ + inprocess::{InProcessHooks, GLOBAL_STATE}, + ExecutorHooksTuple, + }, + Executor, ExitKind, HasObservers, + }, feedbacks::Feedback, fuzzer::HasObjective, inputs::UsesInput, observers::{ObserversTuple, UsesObservers}, - state::{HasCorpus, HasExecutions, HasSolutions, State, UsesState}, + state::{HasCorpus, HasExecutions, HasMetadata, HasSolutions, State, UsesState}, Error, }; /// The process executor simply calls a target function, as mutable reference to a closure -pub type InProcessExecutor<'a, H, OT, S> = GenericInProcessExecutor; +pub type InProcessExecutor<'a, H, OT, S> = GenericInProcessExecutor; /// The process executor simply calls a target function, as boxed `FnMut` trait object pub type OwnedInProcessExecutor = GenericInProcessExecutor< dyn FnMut(&::Input) -> ExitKind, Box::Input) -> ExitKind>, + (), OT, S, >; /// The inmem executor simply calls a target function, then returns afterwards. #[allow(dead_code)] -pub struct GenericInProcessExecutor +pub struct GenericInProcessExecutor where H: FnMut(&S::Input) -> ExitKind + ?Sized, HB: BorrowMut, + HT: ExecutorHooksTuple, OT: ObserversTuple, S: State, { @@ -84,14 +68,15 @@ where /// The observers, observing each run observers: OT, // Crash and timeout hah - handlers: InProcessHandlers, + hooks: (InProcessHooks, HT), phantom: PhantomData<(S, *const H)>, } -impl Debug for GenericInProcessExecutor +impl Debug for GenericInProcessExecutor where H: FnMut(&S::Input) -> ExitKind + ?Sized, HB: BorrowMut, + HT: ExecutorHooksTuple, OT: ObserversTuple + Debug, S: State, { @@ -103,31 +88,34 @@ where } } -impl UsesState for GenericInProcessExecutor +impl UsesState for GenericInProcessExecutor where H: ?Sized + FnMut(&S::Input) -> ExitKind, HB: BorrowMut, + HT: ExecutorHooksTuple, OT: ObserversTuple, S: State, { type State = S; } -impl UsesObservers for GenericInProcessExecutor +impl UsesObservers for GenericInProcessExecutor where H: ?Sized + FnMut(&S::Input) -> ExitKind, HB: BorrowMut, + HT: ExecutorHooksTuple, OT: ObserversTuple, S: State, { type Observers = OT; } -impl Executor for GenericInProcessExecutor +impl Executor for GenericInProcessExecutor where + EM: UsesState, H: FnMut(&S::Input) -> ExitKind + ?Sized, HB: BorrowMut, - EM: UsesState, + HT: ExecutorHooksTuple, OT: ObserversTuple, S: State + HasExecutions, Z: UsesState, @@ -140,20 +128,22 @@ where input: &Self::Input, ) -> Result { *state.executions_mut() += 1; - self.handlers - .pre_run_target(self, fuzzer, state, mgr, input); + self.enter_target(fuzzer, state, mgr, input); + self.hooks.pre_exec_all(fuzzer, state, mgr, input); let ret = (self.harness_fn.borrow_mut())(input); - self.handlers.post_run_target(); + self.hooks.post_exec_all(fuzzer, state, mgr, input); + self.leave_target(fuzzer, state, mgr, input); Ok(ret) } } -impl HasObservers for GenericInProcessExecutor +impl HasObservers for GenericInProcessExecutor where H: FnMut(&S::Input) -> ExitKind + ?Sized, HB: BorrowMut, + HT: ExecutorHooksTuple, OT: ObserversTuple, S: State, { @@ -167,26 +157,70 @@ where &mut self.observers } } +impl GenericInProcessExecutor +where + H: FnMut(&S::Input) -> ExitKind + ?Sized, + HB: BorrowMut, + HT: ExecutorHooksTuple, + OT: ObserversTuple, + S: State, +{ + /// This function marks the boundary between the fuzzer and the target + #[inline] + pub fn enter_target( + &mut self, + fuzzer: &mut Z, + state: &mut ::State, + mgr: &mut EM, + input: &::Input, + ) { + unsafe { + let data = &mut GLOBAL_STATE; + write_volatile( + &mut data.current_input_ptr, + input as *const _ as *const c_void, + ); + write_volatile(&mut data.executor_ptr, self as *const _ as *const c_void); + // Direct raw pointers access /aliasing is pretty undefined behavior. + // Since the state and event may have moved in memory, refresh them right before the signal may happen + write_volatile(&mut data.state_ptr, state as *mut _ as *mut c_void); + write_volatile(&mut data.event_mgr_ptr, mgr as *mut _ as *mut c_void); + write_volatile(&mut data.fuzzer_ptr, fuzzer as *mut _ as *mut c_void); + compiler_fence(Ordering::SeqCst); + } + } -impl GenericInProcessExecutor + /// This function marks the boundary between the fuzzer and the target + #[inline] + pub fn leave_target( + &mut self, + _fuzzer: &mut Z, + _state: &mut ::State, + _mgr: &mut EM, + _input: &::Input, + ) { + unsafe { + let data = &mut GLOBAL_STATE; + + write_volatile(&mut data.current_input_ptr, null()); + compiler_fence(Ordering::SeqCst); + } + } +} + +impl<'a, H, OT, S> InProcessExecutor<'a, H, OT, S> where H: FnMut(&::Input) -> ExitKind + ?Sized, - HB: BorrowMut, OT: ObserversTuple, S: HasExecutions + HasSolutions + HasCorpus + State, { - /// Create a new in mem executor. - /// Caution: crash and restart in one of them will lead to odd behavior if multiple are used, - /// depending on different corpus or state. - /// * `harness_fn` - the harness, executing the function - /// * `observers` - the observers observing the target during execution - /// This may return an error on unix, if signal handler setup fails + /// Create a new in mem executor with the default timeout (5 sec) pub fn new( - harness_fn: HB, + harness_fn: &'a mut H, observers: OT, - _fuzzer: &mut Z, - _state: &mut S, - _event_mgr: &mut EM, + fuzzer: &mut Z, + state: &mut S, + event_mgr: &mut EM, ) -> Result where Self: Executor, @@ -195,7 +229,73 @@ where S: State, Z: HasObjective, { - let handlers = InProcessHandlers::new::()?; + Self::with_timeout_generic( + tuple_list!(), + harness_fn, + observers, + fuzzer, + state, + event_mgr, + Duration::from_millis(5000), + ) + } + + /// Create a new in mem executor with the default timeout and use batch mode(5 sec) + #[cfg(all(feature = "std", target_os = "linux"))] + pub fn batched_timeouts( + harness_fn: &'a mut H, + observers: OT, + fuzzer: &mut Z, + state: &mut S, + event_mgr: &mut EM, + exec_tmout: Duration, + ) -> Result + where + Self: Executor, + EM: EventFirer + EventRestarter, + OF: Feedback, + S: State, + Z: HasObjective, + { + let mut me = Self::with_timeout_generic( + tuple_list!(), + harness_fn, + observers, + fuzzer, + state, + event_mgr, + exec_tmout, + )?; + me.hooks_mut().0.timer_mut().batch_mode = true; + Ok(me) + } + + /// Create a new in mem executor. + /// Caution: crash and restart in one of them will lead to odd behavior if multiple are used, + /// depending on different corpus or state. + /// * `user_hooks` - the hooks run before and after the harness's execution + /// * `harness_fn` - the harness, executing the function + /// * `observers` - the observers observing the target during execution + /// This may return an error on unix, if signal handler setup fails + pub fn with_timeout( + harness_fn: &'a mut H, + observers: OT, + _fuzzer: &mut Z, + state: &mut S, + _event_mgr: &mut EM, + timeout: Duration, + ) -> Result + where + Self: Executor, + EM: EventFirer + EventRestarter, + OF: Feedback, + S: State, + Z: HasObjective, + { + let default = InProcessHooks::new::(timeout)?; + let mut hooks = tuple_list!(default).merge(tuple_list!()); + hooks.init_all::(state); + #[cfg(windows)] // Some initialization necessary for windows. unsafe { @@ -212,10 +312,136 @@ where let mut stack_reserved = 0x20000; SetThreadStackGuarantee(&mut stack_reserved)?; } + + #[cfg(all(feature = "std", windows))] + { + // set timeout for the handler + *hooks.0.millis_sec_mut() = timeout.as_millis() as i64; + } + Ok(Self { harness_fn, observers, - handlers, + hooks, + phantom: PhantomData, + }) + } +} + +impl GenericInProcessExecutor +where + H: FnMut(&::Input) -> ExitKind + ?Sized, + HB: BorrowMut, + HT: ExecutorHooksTuple, + OT: ObserversTuple, + S: HasExecutions + HasSolutions + HasCorpus + State, +{ + /// Create a new in mem executor with the default timeout (5 sec) + pub fn generic( + user_hooks: HT, + harness_fn: HB, + observers: OT, + fuzzer: &mut Z, + state: &mut S, + event_mgr: &mut EM, + ) -> Result + where + Self: Executor, + EM: EventFirer + EventRestarter, + OF: Feedback, + S: State, + Z: HasObjective, + { + Self::with_timeout_generic( + user_hooks, + harness_fn, + observers, + fuzzer, + state, + event_mgr, + Duration::from_millis(5000), + ) + } + + /// Create a new in mem executor with the default timeout and use batch mode(5 sec) + #[cfg(all(feature = "std", target_os = "linux"))] + pub fn batched_timeout_generic( + user_hooks: HT, + harness_fn: HB, + observers: OT, + fuzzer: &mut Z, + state: &mut S, + event_mgr: &mut EM, + exec_tmout: Duration, + ) -> Result + where + Self: Executor, + EM: EventFirer + EventRestarter, + OF: Feedback, + S: State, + Z: HasObjective, + { + let mut me = Self::with_timeout_generic( + user_hooks, harness_fn, observers, fuzzer, state, event_mgr, exec_tmout, + )?; + me.hooks_mut().0.timer_mut().batch_mode = true; + Ok(me) + } + + /// Create a new in mem executor. + /// Caution: crash and restart in one of them will lead to odd behavior if multiple are used, + /// depending on different corpus or state. + /// * `user_hooks` - the hooks run before and after the harness's execution + /// * `harness_fn` - the harness, executing the function + /// * `observers` - the observers observing the target during execution + /// This may return an error on unix, if signal handler setup fails + pub fn with_timeout_generic( + user_hooks: HT, + harness_fn: HB, + observers: OT, + _fuzzer: &mut Z, + state: &mut S, + _event_mgr: &mut EM, + timeout: Duration, + ) -> Result + where + Self: Executor, + EM: EventFirer + EventRestarter, + OF: Feedback, + S: State, + Z: HasObjective, + { + let default = InProcessHooks::new::(timeout)?; + let mut hooks = tuple_list!(default).merge(user_hooks); + hooks.init_all::(state); + + #[cfg(windows)] + // Some initialization necessary for windows. + unsafe { + /* + See https://github.com/AFLplusplus/LibAFL/pull/403 + This one reserves certain amount of memory for the stack. + If stack overflow happens during fuzzing on windows, the program is transferred to our exception handler for windows. + However, if we run out of the stack memory again in this exception handler, we'll crash with STATUS_ACCESS_VIOLATION. + We need this API call because with the llmp_compression + feature enabled, the exception handler uses a lot of stack memory (in the compression lib code) on release build. + As far as I have observed, the compression uses around 0x10000 bytes, but for safety let's just reserve 0x20000 bytes for our exception handlers. + This number 0x20000 could vary depending on the compilers optimization for future compression library changes. + */ + let mut stack_reserved = 0x20000; + SetThreadStackGuarantee(&mut stack_reserved)?; + } + + #[cfg(all(feature = "std", windows))] + { + // set timeout for the handler + *hooks.0.millis_sec_mut() = timeout.as_millis() as i64; + } + + Ok(Self { + harness_fn, + observers, + hooks, phantom: PhantomData, }) } @@ -234,370 +460,47 @@ where /// The inprocess handlers #[inline] - pub fn handlers(&self) -> &InProcessHandlers { - &self.handlers + pub fn hooks(&self) -> &(InProcessHooks, HT) { + &self.hooks } /// The inprocess handlers (mutable) #[inline] - pub fn handlers_mut(&mut self) -> &mut InProcessHandlers { - &mut self.handlers + pub fn hooks_mut(&mut self) -> &mut (InProcessHooks, HT) { + &mut self.hooks } } -/// The struct has [`InProcessHandlers`]. -#[cfg(windows)] -pub trait HasInProcessHandlers { +/// The struct has [`InProcessHooks`]. +pub trait HasInProcessHooks { /// Get the in-process handlers. - fn inprocess_handlers(&self) -> &InProcessHandlers; + fn inprocess_hooks(&self) -> &InProcessHooks; + + /// Get the mut in-process handlers. + fn inprocess_hooks_mut(&mut self) -> &mut InProcessHooks; } -#[cfg(windows)] -impl HasInProcessHandlers for GenericInProcessExecutor +impl HasInProcessHooks for GenericInProcessExecutor where H: FnMut(&::Input) -> ExitKind + ?Sized, HB: BorrowMut, + HT: ExecutorHooksTuple, OT: ObserversTuple, S: State + HasExecutions + HasSolutions + HasCorpus, { /// the timeout handler #[inline] - fn inprocess_handlers(&self) -> &InProcessHandlers { - &self.handlers + fn inprocess_hooks(&self) -> &InProcessHooks { + &self.hooks.0 + } + + /// the timeout handler + #[inline] + fn inprocess_hooks_mut(&mut self) -> &mut InProcessHooks { + &mut self.hooks.0 } } -/// The inmem executor's handlers. -#[derive(Debug)] -pub struct InProcessHandlers { - /// On crash C function pointer - #[cfg(any(unix, feature = "std"))] - pub crash_handler: *const c_void, - /// On timeout C function pointer - #[cfg(any(unix, feature = "std"))] - pub timeout_handler: *const c_void, -} - -/// The common signals we want to handle -#[cfg(unix)] -#[inline] -fn common_signals() -> Vec { - vec![ - Signal::SigAlarm, - Signal::SigUser2, - Signal::SigAbort, - Signal::SigBus, - #[cfg(feature = "handle_sigpipe")] - Signal::SigPipe, - Signal::SigFloatingPointException, - Signal::SigIllegalInstruction, - Signal::SigSegmentationFault, - Signal::SigTrap, - ] -} - -impl InProcessHandlers { - /// Call before running a target. - #[allow(clippy::unused_self)] - pub fn pre_run_target( - &self, - _executor: &E, - _fuzzer: &mut Z, - _state: &mut S, - _mgr: &mut EM, - _input: &I, - ) { - #[cfg(unix)] - unsafe { - let data = &mut GLOBAL_STATE; - write_volatile( - &mut data.current_input_ptr, - _input as *const _ as *const c_void, - ); - write_volatile( - &mut data.executor_ptr, - _executor as *const _ as *const c_void, - ); - data.crash_handler = self.crash_handler; - data.timeout_handler = self.timeout_handler; - // Direct raw pointers access /aliasing is pretty undefined behavior. - // Since the state and event may have moved in memory, refresh them right before the signal may happen - write_volatile(&mut data.state_ptr, _state as *mut _ as *mut c_void); - write_volatile(&mut data.event_mgr_ptr, _mgr as *mut _ as *mut c_void); - write_volatile(&mut data.fuzzer_ptr, _fuzzer as *mut _ as *mut c_void); - compiler_fence(Ordering::SeqCst); - } - #[cfg(all(windows, feature = "std"))] - unsafe { - let data = &mut GLOBAL_STATE; - write_volatile( - &mut data.current_input_ptr, - _input as *const _ as *const c_void, - ); - write_volatile( - &mut data.executor_ptr, - _executor as *const _ as *const c_void, - ); - data.crash_handler = self.crash_handler; - data.timeout_handler = self.timeout_handler; - // Direct raw pointers access /aliasing is pretty undefined behavior. - // Since the state and event may have moved in memory, refresh them right before the signal may happen - write_volatile(&mut data.state_ptr, _state as *mut _ as *mut c_void); - write_volatile(&mut data.event_mgr_ptr, _mgr as *mut _ as *mut c_void); - write_volatile(&mut data.fuzzer_ptr, _fuzzer as *mut _ as *mut c_void); - compiler_fence(Ordering::SeqCst); - } - } - - /// Call after running a target. - #[allow(clippy::unused_self)] - pub fn post_run_target(&self) { - #[cfg(unix)] - unsafe { - write_volatile(&mut GLOBAL_STATE.current_input_ptr, ptr::null()); - compiler_fence(Ordering::SeqCst); - } - #[cfg(all(windows, feature = "std"))] - unsafe { - write_volatile(&mut GLOBAL_STATE.current_input_ptr, ptr::null()); - compiler_fence(Ordering::SeqCst); - } - } - - /// Create new [`InProcessHandlers`]. - #[cfg(not(all(windows, feature = "std")))] - pub fn new() -> Result - where - E: Executor + HasObservers, - EM: EventFirer + EventRestarter, - OF: Feedback, - E::State: HasExecutions + HasSolutions + HasCorpus, - Z: HasObjective, - { - #[cfg(unix)] - #[cfg_attr(miri, allow(unused_variables))] - unsafe { - let data = &mut GLOBAL_STATE; - #[cfg(feature = "std")] - unix_signal_handler::setup_panic_hook::(); - #[cfg(not(miri))] - setup_signal_handler(data)?; - compiler_fence(Ordering::SeqCst); - Ok(Self { - crash_handler: unix_signal_handler::inproc_crash_handler:: - as *const c_void, - timeout_handler: unix_signal_handler::inproc_timeout_handler:: - as *const _, - }) - } - #[cfg(not(any(unix, feature = "std")))] - Ok(Self {}) - } - - /// Create new [`InProcessHandlers`]. - #[cfg(all(windows, feature = "std"))] - pub fn new() -> Result - where - E: Executor + HasObservers + HasInProcessHandlers, - EM: EventFirer + EventRestarter, - OF: Feedback, - E::State: State + HasExecutions + HasSolutions + HasCorpus, - Z: HasObjective, - { - unsafe { - let data = &mut GLOBAL_STATE; - #[cfg(feature = "std")] - windows_exception_handler::setup_panic_hook::(); - setup_exception_handler(data)?; - compiler_fence(Ordering::SeqCst); - - Ok(Self { - crash_handler: windows_exception_handler::inproc_crash_handler:: - as *const _, - timeout_handler: windows_exception_handler::inproc_timeout_handler:: - as *const c_void, - }) - } - } - - /// Replace the handlers with `nop` handlers, deactivating the handlers - #[must_use] - pub fn nop() -> Self { - let ret; - #[cfg(any(unix, feature = "std"))] - { - ret = Self { - crash_handler: ptr::null(), - timeout_handler: ptr::null(), - }; - } - #[cfg(not(any(unix, feature = "std")))] - { - ret = Self {}; - } - ret - } -} - -/// The global state of the in-process harness. -#[derive(Debug)] -pub struct InProcessExecutorHandlerData { - state_ptr: *mut c_void, - event_mgr_ptr: *mut c_void, - fuzzer_ptr: *mut c_void, - executor_ptr: *const c_void, - pub(crate) current_input_ptr: *const c_void, - pub(crate) in_handler: bool, - - /// The timeout handler - #[cfg(any(unix, feature = "std"))] - crash_handler: *const c_void, - /// The timeout handler - #[cfg(any(unix, feature = "std"))] - timeout_handler: *const c_void, - - #[cfg(all(windows, feature = "std"))] - pub(crate) ptp_timer: Option, - #[cfg(all(windows, feature = "std"))] - pub(crate) in_target: u64, - #[cfg(all(windows, feature = "std"))] - pub(crate) critical: *mut c_void, - #[cfg(all(windows, feature = "std"))] - pub(crate) timeout_input_ptr: *mut c_void, - - #[cfg(any(unix, feature = "std"))] - pub(crate) timeout_executor_ptr: *mut c_void, -} - -unsafe impl Send for InProcessExecutorHandlerData {} -unsafe impl Sync for InProcessExecutorHandlerData {} - -impl InProcessExecutorHandlerData { - #[cfg(any(unix, feature = "std"))] - fn executor_mut<'a, E>(&self) -> &'a mut E { - unsafe { (self.executor_ptr as *mut E).as_mut().unwrap() } - } - - #[cfg(any(unix, feature = "std"))] - fn state_mut<'a, S>(&self) -> &'a mut S { - unsafe { (self.state_ptr as *mut S).as_mut().unwrap() } - } - - #[cfg(any(unix, feature = "std"))] - fn event_mgr_mut<'a, EM>(&self) -> &'a mut EM { - unsafe { (self.event_mgr_ptr as *mut EM).as_mut().unwrap() } - } - - #[cfg(any(unix, feature = "std"))] - fn fuzzer_mut<'a, Z>(&self) -> &'a mut Z { - unsafe { (self.fuzzer_ptr as *mut Z).as_mut().unwrap() } - } - - #[cfg(any(unix, feature = "std"))] - fn take_current_input<'a, I>(&mut self) -> &'a I { - let r = unsafe { (self.current_input_ptr as *const I).as_ref().unwrap() }; - self.current_input_ptr = ptr::null(); - r - } - - #[cfg(any(unix, feature = "std"))] - pub(crate) fn is_valid(&self) -> bool { - !self.current_input_ptr.is_null() - } - - #[cfg(any(unix, feature = "std"))] - fn timeout_executor_mut<'a, E>(&self) -> &'a mut crate::executors::timeout::TimeoutExecutor { - unsafe { - (self.timeout_executor_ptr as *mut crate::executors::timeout::TimeoutExecutor) - .as_mut() - .unwrap() - } - } - - #[cfg(any(unix, feature = "std"))] - fn set_in_handler(&mut self, v: bool) -> bool { - let old = self.in_handler; - self.in_handler = v; - old - } -} - -/// Exception handling needs some nasty unsafe. -pub(crate) static mut GLOBAL_STATE: InProcessExecutorHandlerData = InProcessExecutorHandlerData { - // The state ptr for signal handling - state_ptr: null_mut(), - // The event manager ptr for signal handling - event_mgr_ptr: null_mut(), - // The fuzzer ptr for signal handling - fuzzer_ptr: null_mut(), - // The executor ptr for signal handling - executor_ptr: ptr::null(), - // The current input for signal handling - current_input_ptr: ptr::null(), - - in_handler: false, - - // The crash handler fn - #[cfg(any(unix, feature = "std"))] - crash_handler: ptr::null(), - // The timeout handler fn - #[cfg(any(unix, feature = "std"))] - timeout_handler: ptr::null(), - #[cfg(all(windows, feature = "std"))] - ptp_timer: None, - #[cfg(all(windows, feature = "std"))] - in_target: 0, - #[cfg(all(windows, feature = "std"))] - critical: null_mut(), - #[cfg(all(windows, feature = "std"))] - timeout_input_ptr: null_mut(), - - #[cfg(any(unix, feature = "std"))] - timeout_executor_ptr: null_mut(), -}; - -/// Get the inprocess [`crate::state::State`] -#[must_use] -pub fn inprocess_get_state<'a, S>() -> Option<&'a mut S> { - unsafe { (GLOBAL_STATE.state_ptr as *mut S).as_mut() } -} - -/// Get the [`crate::events::EventManager`] -#[must_use] -pub fn inprocess_get_event_manager<'a, EM>() -> Option<&'a mut EM> { - unsafe { (GLOBAL_STATE.event_mgr_ptr as *mut EM).as_mut() } -} - -/// Gets the inprocess [`crate::fuzzer::Fuzzer`] -#[must_use] -pub fn inprocess_get_fuzzer<'a, F>() -> Option<&'a mut F> { - unsafe { (GLOBAL_STATE.fuzzer_ptr as *mut F).as_mut() } -} - -/// Gets the inprocess [`Executor`] -#[must_use] -pub fn inprocess_get_executor<'a, E>() -> Option<&'a mut E> { - unsafe { (GLOBAL_STATE.executor_ptr as *mut E).as_mut() } -} - -/// Gets the inprocess input -#[must_use] -pub fn inprocess_get_input<'a, I>() -> Option<&'a I> { - unsafe { (GLOBAL_STATE.current_input_ptr as *const I).as_ref() } -} - -/// Know if we ar eexecuting in a crash/timeout handler -#[must_use] -pub fn inprocess_in_handler() -> bool { - unsafe { GLOBAL_STATE.in_handler } -} - -use crate::{ - corpus::{Corpus, Testcase}, - events::Event, - state::HasMetadata, -}; - #[inline] #[allow(clippy::too_many_arguments)] /// Save state if it is an objective @@ -670,8 +573,6 @@ where if data.is_valid() { let executor = data.executor_mut::(); - // disarms timeout in case of TimeoutExecutor - executor.post_run_reset(); let state = data.state_mut::(); let event_mgr = data.event_mgr_mut::(); let fuzzer = data.fuzzer_mut::(); @@ -690,1506 +591,19 @@ where data.set_in_handler(in_handler); } -/// The inprocess executor singal handling code for unix -#[cfg(unix)] -pub mod unix_signal_handler { - use alloc::vec::Vec; - #[cfg(feature = "std")] - use alloc::{boxed::Box, string::String}; - use core::mem::transmute; - #[cfg(feature = "std")] - use std::{io::Write, panic}; - - use libafl_bolts::os::unix_signals::{ucontext_t, Handler, Signal}; - use libc::siginfo_t; - - use super::common_signals; - #[cfg(feature = "std")] - use crate::inputs::Input; - use crate::{ - events::{EventFirer, EventRestarter}, - executors::{ - inprocess::{run_observers_and_save_state, InProcessExecutorHandlerData, GLOBAL_STATE}, - Executor, ExitKind, HasObservers, - }, - feedbacks::Feedback, - fuzzer::HasObjective, - inputs::UsesInput, - state::{HasCorpus, HasExecutions, HasSolutions}, - }; - - pub(crate) type HandlerFuncPtr = unsafe fn( - Signal, - &mut siginfo_t, - Option<&mut ucontext_t>, - data: &mut InProcessExecutorHandlerData, - ); - - /// A handler that does nothing. - /*pub fn nop_handler( - _signal: Signal, - _info: &mut siginfo_t, - _context: Option<&mut ucontext_t>, - _data: &mut InProcessExecutorHandlerData, - ) { - }*/ - - #[cfg(unix)] - impl Handler for InProcessExecutorHandlerData { - fn handle( - &mut self, - signal: Signal, - info: &mut siginfo_t, - context: Option<&mut ucontext_t>, - ) { - unsafe { - let data = &mut GLOBAL_STATE; - let in_handler = data.set_in_handler(true); - match signal { - Signal::SigUser2 | Signal::SigAlarm => { - if !data.timeout_handler.is_null() { - let func: HandlerFuncPtr = transmute(data.timeout_handler); - (func)(signal, info, context, data); - } - } - _ => { - if !data.crash_handler.is_null() { - let func: HandlerFuncPtr = transmute(data.crash_handler); - (func)(signal, info, context, data); - } - } - } - data.set_in_handler(in_handler); - } - } - - fn signals(&self) -> Vec { - common_signals() - } - } - - /// invokes the `post_exec` hook on all observer in case of panic - #[cfg(feature = "std")] - pub fn setup_panic_hook() - where - E: HasObservers, - EM: EventFirer + EventRestarter, - OF: Feedback, - E::State: HasExecutions + HasSolutions + HasCorpus, - Z: HasObjective, - { - let old_hook = panic::take_hook(); - panic::set_hook(Box::new(move |panic_info| { - old_hook(panic_info); - let data = unsafe { &mut GLOBAL_STATE }; - let in_handler = data.set_in_handler(true); - if data.is_valid() { - // We are fuzzing! - let executor = data.executor_mut::(); - let state = data.state_mut::(); - let input = data.take_current_input::<::Input>(); - let fuzzer = data.fuzzer_mut::(); - let event_mgr = data.event_mgr_mut::(); - - run_observers_and_save_state::( - executor, - state, - input, - fuzzer, - event_mgr, - ExitKind::Crash, - ); - - unsafe { - libc::_exit(128 + 6); - } // SIGABRT exit code - } - data.set_in_handler(in_handler); - })); - } - - /// Timeout-Handler for in-process fuzzing. - /// It will store the current State to shmem, then exit. - /// - /// # Safety - /// Well, signal handling is not safe - #[cfg(unix)] - pub unsafe fn inproc_timeout_handler( - _signal: Signal, - _info: &mut siginfo_t, - _context: Option<&mut ucontext_t>, - data: &mut InProcessExecutorHandlerData, - ) where - E: HasObservers, - EM: EventFirer + EventRestarter, - OF: Feedback, - E::State: HasExecutions + HasSolutions + HasCorpus, - Z: HasObjective, - { - if !data.timeout_executor_ptr.is_null() - && data.timeout_executor_mut::().handle_timeout(data) - { - return; - } - - if !data.is_valid() { - log::warn!("TIMEOUT or SIGUSR2 happened, but currently not fuzzing."); - return; - } - - let executor = data.executor_mut::(); - let state = data.state_mut::(); - let event_mgr = data.event_mgr_mut::(); - let fuzzer = data.fuzzer_mut::(); - let input = data.take_current_input::<::Input>(); - - log::error!("Timeout in fuzz run."); - - run_observers_and_save_state::( - executor, - state, - input, - fuzzer, - event_mgr, - ExitKind::Timeout, - ); - - libc::_exit(55); - } - - /// Crash-Handler for in-process fuzzing. - /// Will be used for signal handling. - /// It will store the current State to shmem, then exit. - /// - /// # Safety - /// Well, signal handling is not safe - #[allow(clippy::too_many_lines)] - pub unsafe fn inproc_crash_handler( - signal: Signal, - _info: &mut siginfo_t, - _context: Option<&mut ucontext_t>, - data: &mut InProcessExecutorHandlerData, - ) where - E: Executor + HasObservers, - EM: EventFirer + EventRestarter, - OF: Feedback, - E::State: HasExecutions + HasSolutions + HasCorpus, - Z: HasObjective, - { - #[cfg(all(target_os = "android", target_arch = "aarch64"))] - let _context = _context.map(|p| { - &mut *(((p as *mut _ as *mut libc::c_void as usize) + 128) as *mut libc::c_void - as *mut ucontext_t) - }); - - log::error!("Crashed with {signal}"); - if data.is_valid() { - let executor = data.executor_mut::(); - // disarms timeout in case of TimeoutExecutor - executor.post_run_reset(); - let state = data.state_mut::(); - let event_mgr = data.event_mgr_mut::(); - let fuzzer = data.fuzzer_mut::(); - let input = data.take_current_input::<::Input>(); - - log::error!("Child crashed!"); - - #[cfg(all(feature = "std", unix))] - { - let mut bsod = Vec::new(); - { - let mut writer = std::io::BufWriter::new(&mut bsod); - writeln!(writer, "input: {:?}", input.generate_name(0)).unwrap(); - libafl_bolts::minibsod::generate_minibsod( - &mut writer, - signal, - _info, - _context.as_deref(), - ) - .unwrap(); - writer.flush().unwrap(); - } - log::error!("{}", std::str::from_utf8(&bsod).unwrap()); - } - - run_observers_and_save_state::( - executor, - state, - input, - fuzzer, - event_mgr, - ExitKind::Crash, - ); - } else { - { - log::error!("Double crash\n"); - #[cfg(target_os = "android")] - let si_addr = (_info._pad[0] as i64) | ((_info._pad[1] as i64) << 32); - #[cfg(not(target_os = "android"))] - let si_addr = { _info.si_addr() as usize }; - - log::error!( - "We crashed at addr 0x{si_addr:x}, but are not in the target... Bug in the fuzzer? Exiting." - ); - - #[cfg(all(feature = "std", unix))] - { - let mut bsod = Vec::new(); - { - let mut writer = std::io::BufWriter::new(&mut bsod); - libafl_bolts::minibsod::generate_minibsod( - &mut writer, - signal, - _info, - _context.as_deref(), - ) - .unwrap(); - writer.flush().unwrap(); - } - log::error!("{}", std::str::from_utf8(&bsod).unwrap()); - } - } - - #[cfg(feature = "std")] - { - log::error!("Type QUIT to restart the child"); - let mut line = String::new(); - while line.trim() != "QUIT" { - std::io::stdin().read_line(&mut line).unwrap(); - } - } - - // TODO tell the parent to not restart - } - - libc::_exit(128 + (signal as i32)); - } -} - -/// Same as `inproc_crash_handler`, but this is called when address sanitizer exits, not from the exception handler -#[cfg(all(windows, feature = "std"))] -pub mod windows_asan_handler { - use alloc::string::String; - use core::sync::atomic::{compiler_fence, Ordering}; - - use windows::Win32::System::Threading::{ - EnterCriticalSection, LeaveCriticalSection, CRITICAL_SECTION, - }; - - use crate::{ - events::{EventFirer, EventRestarter}, - executors::{ - inprocess::{run_observers_and_save_state, GLOBAL_STATE}, - Executor, ExitKind, HasObservers, - }, - feedbacks::Feedback, - fuzzer::HasObjective, - inputs::UsesInput, - state::{HasCorpus, HasExecutions, HasSolutions}, - }; - - /// # Safety - /// ASAN deatch handler - pub unsafe extern "C" fn asan_death_handler() - where - E: Executor + HasObservers, - EM: EventFirer + EventRestarter, - OF: Feedback, - E::State: HasExecutions + HasSolutions + HasCorpus, - Z: HasObjective, - { - let data = &mut GLOBAL_STATE; - data.set_in_handler(true); - // Have we set a timer_before? - if data.ptp_timer.is_some() { - /* - We want to prevent the timeout handler being run while the main thread is executing the crash handler - Timeout handler runs if it has access to the critical section or data.in_target == 0 - Writing 0 to the data.in_target makes the timeout handler makes the timeout handler invalid. - */ - compiler_fence(Ordering::SeqCst); - EnterCriticalSection(data.critical as *mut CRITICAL_SECTION); - compiler_fence(Ordering::SeqCst); - data.in_target = 0; - compiler_fence(Ordering::SeqCst); - LeaveCriticalSection(data.critical as *mut CRITICAL_SECTION); - compiler_fence(Ordering::SeqCst); - } - - log::error!("ASAN detected crash!"); - if data.current_input_ptr.is_null() { - { - log::error!("Double crash\n"); - log::error!( - "ASAN detected crash but we're not in the target... Bug in the fuzzer? Exiting.", - ); - } - #[cfg(feature = "std")] - { - log::error!("Type QUIT to restart the child"); - let mut line = String::new(); - while line.trim() != "QUIT" { - std::io::stdin().read_line(&mut line).unwrap(); - } - } - - // TODO tell the parent to not restart - } else { - let executor = data.executor_mut::(); - // reset timer - if data.ptp_timer.is_some() { - executor.post_run_reset(); - data.ptp_timer = None; - } - - let state = data.state_mut::(); - let fuzzer = data.fuzzer_mut::(); - let event_mgr = data.event_mgr_mut::(); - - log::error!("Child crashed!"); - - // Make sure we don't crash in the crash handler forever. - let input = data.take_current_input::<::Input>(); - - run_observers_and_save_state::( - executor, - state, - input, - fuzzer, - event_mgr, - ExitKind::Crash, - ); - } - // Don't need to exit, Asan will exit for us - // ExitProcess(1); - } -} - -/// Handles exceptions on Windows -#[cfg(all(windows, feature = "std"))] -pub mod windows_exception_handler { - #[cfg(feature = "std")] - use alloc::boxed::Box; - use alloc::{string::String, vec::Vec}; - use core::{ - ffi::c_void, - mem::transmute, - ptr, - sync::atomic::{compiler_fence, Ordering}, - }; - #[cfg(feature = "std")] - use std::panic; - - use libafl_bolts::os::windows_exceptions::{ - ExceptionCode, Handler, CRASH_EXCEPTIONS, EXCEPTION_HANDLERS_SIZE, EXCEPTION_POINTERS, - }; - use windows::Win32::System::Threading::{ - EnterCriticalSection, ExitProcess, LeaveCriticalSection, CRITICAL_SECTION, - }; - - use crate::{ - events::{EventFirer, EventRestarter}, - executors::{ - inprocess::{ - run_observers_and_save_state, HasInProcessHandlers, InProcessExecutorHandlerData, - GLOBAL_STATE, - }, - Executor, ExitKind, HasObservers, - }, - feedbacks::Feedback, - fuzzer::HasObjective, - inputs::UsesInput, - state::{HasCorpus, HasExecutions, HasSolutions, State}, - }; - - pub(crate) type HandlerFuncPtr = - unsafe fn(*mut EXCEPTION_POINTERS, &mut InProcessExecutorHandlerData); - - /*pub unsafe fn nop_handler( - _code: ExceptionCode, - _exception_pointers: *mut EXCEPTION_POINTERS, - _data: &mut InProcessExecutorHandlerData, - ) { - }*/ - - impl Handler for InProcessExecutorHandlerData { - #[allow(clippy::not_unsafe_ptr_arg_deref)] - fn handle(&mut self, _code: ExceptionCode, exception_pointers: *mut EXCEPTION_POINTERS) { - unsafe { - let data = &mut GLOBAL_STATE; - let in_handler = data.set_in_handler(true); - if !data.crash_handler.is_null() { - let func: HandlerFuncPtr = transmute(data.crash_handler); - (func)(exception_pointers, data); - } - data.set_in_handler(in_handler); - } - } - - fn exceptions(&self) -> Vec { - let crash_list = CRASH_EXCEPTIONS.to_vec(); - assert!(crash_list.len() < EXCEPTION_HANDLERS_SIZE - 1); - crash_list - } - } - - /// invokes the `post_exec` hook on all observer in case of panic - /// - /// # Safety - /// Well, exception handling is not safe - #[cfg(feature = "std")] - pub fn setup_panic_hook() - where - E: HasObservers, - EM: EventFirer + EventRestarter, - OF: Feedback, - E::State: HasExecutions + HasSolutions + HasCorpus, - Z: HasObjective, - { - let old_hook = panic::take_hook(); - panic::set_hook(Box::new(move |panic_info| { - let data = unsafe { &mut GLOBAL_STATE }; - let in_handler = data.set_in_handler(true); - // Have we set a timer_before? - unsafe { - if data.ptp_timer.is_some() { - /* - We want to prevent the timeout handler being run while the main thread is executing the crash handler - Timeout handler runs if it has access to the critical section or data.in_target == 0 - Writing 0 to the data.in_target makes the timeout handler makes the timeout handler invalid. - */ - compiler_fence(Ordering::SeqCst); - EnterCriticalSection(data.critical as *mut CRITICAL_SECTION); - compiler_fence(Ordering::SeqCst); - data.in_target = 0; - compiler_fence(Ordering::SeqCst); - LeaveCriticalSection(data.critical as *mut CRITICAL_SECTION); - compiler_fence(Ordering::SeqCst); - } - } - - if data.is_valid() { - // We are fuzzing! - let executor = data.executor_mut::(); - let state = data.state_mut::(); - let fuzzer = data.fuzzer_mut::(); - let event_mgr = data.event_mgr_mut::(); - - let input = data.take_current_input::<::Input>(); - - run_observers_and_save_state::( - executor, - state, - input, - fuzzer, - event_mgr, - ExitKind::Crash, - ); - - unsafe { - ExitProcess(1); - } - } - old_hook(panic_info); - data.set_in_handler(in_handler); - })); - } - - /// Timeout handler for windows - /// - /// # Safety - /// Well, exception handling is not safe - pub unsafe extern "system" fn inproc_timeout_handler( - _p0: *mut u8, - global_state: *mut c_void, - _p1: *mut u8, - ) where - E: HasObservers + HasInProcessHandlers, - EM: EventFirer + EventRestarter, - OF: Feedback, - E::State: State + HasExecutions + HasSolutions + HasCorpus, - Z: HasObjective, - { - let data: &mut InProcessExecutorHandlerData = - &mut *(global_state as *mut InProcessExecutorHandlerData); - compiler_fence(Ordering::SeqCst); - EnterCriticalSection((data.critical as *mut CRITICAL_SECTION).as_mut().unwrap()); - compiler_fence(Ordering::SeqCst); - - if !data.timeout_executor_ptr.is_null() - && data.timeout_executor_mut::().handle_timeout(data) - { - compiler_fence(Ordering::SeqCst); - LeaveCriticalSection((data.critical as *mut CRITICAL_SECTION).as_mut().unwrap()); - compiler_fence(Ordering::SeqCst); - - return; - } - - if data.in_target == 1 { - let executor = data.executor_mut::(); - let state = data.state_mut::(); - let fuzzer = data.fuzzer_mut::(); - let event_mgr = data.event_mgr_mut::(); - - if data.timeout_input_ptr.is_null() { - log::error!("TIMEOUT or SIGUSR2 happened, but currently not fuzzing. Exiting"); - } else { - log::error!("Timeout in fuzz run."); - - let input = (data.timeout_input_ptr as *const ::Input) - .as_ref() - .unwrap(); - data.timeout_input_ptr = ptr::null_mut(); - - run_observers_and_save_state::( - executor, - state, - input, - fuzzer, - event_mgr, - ExitKind::Timeout, - ); - - compiler_fence(Ordering::SeqCst); - - ExitProcess(1); - } - } - compiler_fence(Ordering::SeqCst); - LeaveCriticalSection((data.critical as *mut CRITICAL_SECTION).as_mut().unwrap()); - compiler_fence(Ordering::SeqCst); - // log::info!("TIMER INVOKED!"); - } - - /// Crash handler for windows - /// - /// # Safety - /// Well, exception handling is not safe - #[allow(clippy::too_many_lines)] - pub unsafe fn inproc_crash_handler( - exception_pointers: *mut EXCEPTION_POINTERS, - data: &mut InProcessExecutorHandlerData, - ) where - E: Executor + HasObservers, - EM: EventFirer + EventRestarter, - OF: Feedback, - E::State: HasExecutions + HasSolutions + HasCorpus, - Z: HasObjective, - { - // Have we set a timer_before? - if data.ptp_timer.is_some() { - /* - We want to prevent the timeout handler being run while the main thread is executing the crash handler - Timeout handler runs if it has access to the critical section or data.in_target == 0 - Writing 0 to the data.in_target makes the timeout handler makes the timeout handler invalid. - */ - compiler_fence(Ordering::SeqCst); - EnterCriticalSection(data.critical as *mut CRITICAL_SECTION); - compiler_fence(Ordering::SeqCst); - data.in_target = 0; - compiler_fence(Ordering::SeqCst); - LeaveCriticalSection(data.critical as *mut CRITICAL_SECTION); - compiler_fence(Ordering::SeqCst); - } - - // Is this really crash? - let mut is_crash = true; - #[cfg(feature = "std")] - if let Some(exception_pointers) = exception_pointers.as_mut() { - let code = ExceptionCode::try_from( - exception_pointers - .ExceptionRecord - .as_mut() - .unwrap() - .ExceptionCode - .0, - ) - .unwrap(); - - let exception_list = data.exceptions(); - if exception_list.contains(&code) { - log::error!("Crashed with {code}"); - } else { - // log::trace!("Exception code received, but {code} is not in CRASH_EXCEPTIONS"); - is_crash = false; - } - } else { - log::error!("Crashed without exception (probably due to SIGABRT)"); - }; - - if data.current_input_ptr.is_null() { - { - log::error!("Double crash\n"); - let crash_addr = exception_pointers - .as_mut() - .unwrap() - .ExceptionRecord - .as_mut() - .unwrap() - .ExceptionAddress as usize; - - log::error!( - "We crashed at addr 0x{crash_addr:x}, but are not in the target... Bug in the fuzzer? Exiting." - ); - } - #[cfg(feature = "std")] - { - log::error!("Type QUIT to restart the child"); - let mut line = String::new(); - while line.trim() != "QUIT" { - std::io::stdin().read_line(&mut line).unwrap(); - } - } - - // TODO tell the parent to not restart - } else { - let executor = data.executor_mut::(); - // reset timer - if data.ptp_timer.is_some() { - executor.post_run_reset(); - data.ptp_timer = None; - } - - let state = data.state_mut::(); - let fuzzer = data.fuzzer_mut::(); - let event_mgr = data.event_mgr_mut::(); - - if is_crash { - log::error!("Child crashed!"); - } else { - // log::info!("Exception received!"); - } - - // Make sure we don't crash in the crash handler forever. - if is_crash { - let input = data.take_current_input::<::Input>(); - - run_observers_and_save_state::( - executor, - state, - input, - fuzzer, - event_mgr, - ExitKind::Crash, - ); - } else { - // This is not worth saving - } - } - - if is_crash { - log::info!("Exiting!"); - ExitProcess(1); - } - // log::info!("Not Exiting!"); - } -} - -/// The signature of the crash handler function -#[cfg(all(feature = "std", unix))] -pub(crate) type ForkHandlerFuncPtr = unsafe fn( - Signal, - &mut siginfo_t, - Option<&mut ucontext_t>, - data: &mut InProcessForkExecutorGlobalData, -); - -/// The inmem fork executor's handlers. -#[cfg(all(feature = "std", unix))] -#[derive(Debug)] -pub struct InChildProcessHandlers { - /// On crash C function pointer - pub crash_handler: *const c_void, - /// On timeout C function pointer - pub timeout_handler: *const c_void, -} - -#[cfg(all(feature = "std", unix))] -impl InChildProcessHandlers { - /// Call before running a target. - pub fn pre_run_target(&self, executor: &E, state: &mut S, input: &I) { - unsafe { - let data = &mut FORK_EXECUTOR_GLOBAL_DATA; - write_volatile( - &mut data.executor_ptr, - executor as *const _ as *const c_void, - ); - write_volatile( - &mut data.current_input_ptr, - input as *const _ as *const c_void, - ); - write_volatile(&mut data.state_ptr, state as *mut _ as *mut c_void); - data.crash_handler = self.crash_handler; - data.timeout_handler = self.timeout_handler; - compiler_fence(Ordering::SeqCst); - } - } - - /// Create new [`InChildProcessHandlers`]. - pub fn new() -> Result - where - E: HasObservers, - { - #[cfg_attr(miri, allow(unused_variables))] - unsafe { - let data = &mut FORK_EXECUTOR_GLOBAL_DATA; - // child_signal_handlers::setup_child_panic_hook::(); - #[cfg(not(miri))] - setup_signal_handler(data)?; - compiler_fence(Ordering::SeqCst); - Ok(Self { - crash_handler: child_signal_handlers::child_crash_handler:: as *const c_void, - timeout_handler: ptr::null(), - }) - } - } - - /// Create new [`InChildProcessHandlers`]. - pub fn with_timeout() -> Result - where - E: HasObservers, - { - #[cfg_attr(miri, allow(unused_variables))] - unsafe { - let data = &mut FORK_EXECUTOR_GLOBAL_DATA; - // child_signal_handlers::setup_child_panic_hook::(); - #[cfg(not(miri))] - setup_signal_handler(data)?; - compiler_fence(Ordering::SeqCst); - Ok(Self { - crash_handler: child_signal_handlers::child_crash_handler:: as *const c_void, - timeout_handler: child_signal_handlers::child_timeout_handler:: as *const c_void, - }) - } - } - - /// Replace the handlers with `nop` handlers, deactivating the handlers - #[must_use] - pub fn nop() -> Self { - Self { - crash_handler: ptr::null(), - timeout_handler: ptr::null(), - } - } -} - -/// The global state of the in-process-fork harness. -#[cfg(all(feature = "std", unix))] -#[derive(Debug)] -pub(crate) struct InProcessForkExecutorGlobalData { - /// Stores a pointer to the fork executor struct - pub executor_ptr: *const c_void, - /// Stores a pointer to the state - pub state_ptr: *const c_void, - /// Stores a pointer to the current input - pub current_input_ptr: *const c_void, - /// Stores a pointer to the crash_handler function - pub crash_handler: *const c_void, - /// Stores a pointer to the timeout_handler function - pub timeout_handler: *const c_void, -} - -#[cfg(all(feature = "std", unix))] -unsafe impl Sync for InProcessForkExecutorGlobalData {} -#[cfg(all(feature = "std", unix))] -unsafe impl Send for InProcessForkExecutorGlobalData {} - -#[cfg(all(feature = "std", unix))] -impl InProcessForkExecutorGlobalData { - fn executor_mut<'a, E>(&self) -> &'a mut E { - unsafe { (self.executor_ptr as *mut E).as_mut().unwrap() } - } - - fn state_mut<'a, S>(&self) -> &'a mut S { - unsafe { (self.state_ptr as *mut S).as_mut().unwrap() } - } - - /*fn current_input<'a, I>(&self) -> &'a I { - unsafe { (self.current_input_ptr as *const I).as_ref().unwrap() } - }*/ - - fn take_current_input<'a, I>(&mut self) -> &'a I { - let r = unsafe { (self.current_input_ptr as *const I).as_ref().unwrap() }; - self.current_input_ptr = ptr::null(); - r - } - - fn is_valid(&self) -> bool { - !self.current_input_ptr.is_null() - } -} - -/// a static variable storing the global state -#[cfg(all(feature = "std", unix))] -pub(crate) static mut FORK_EXECUTOR_GLOBAL_DATA: InProcessForkExecutorGlobalData = - InProcessForkExecutorGlobalData { - executor_ptr: ptr::null(), - state_ptr: ptr::null(), - current_input_ptr: ptr::null(), - crash_handler: ptr::null(), - timeout_handler: ptr::null(), - }; - -#[cfg(all(feature = "std", unix))] -impl Handler for InProcessForkExecutorGlobalData { - fn handle(&mut self, signal: Signal, info: &mut siginfo_t, context: Option<&mut ucontext_t>) { - match signal { - Signal::SigUser2 | Signal::SigAlarm => unsafe { - if !FORK_EXECUTOR_GLOBAL_DATA.timeout_handler.is_null() { - let func: ForkHandlerFuncPtr = - transmute(FORK_EXECUTOR_GLOBAL_DATA.timeout_handler); - (func)(signal, info, context, &mut FORK_EXECUTOR_GLOBAL_DATA); - } - }, - _ => unsafe { - if !FORK_EXECUTOR_GLOBAL_DATA.crash_handler.is_null() { - let func: ForkHandlerFuncPtr = - transmute(FORK_EXECUTOR_GLOBAL_DATA.crash_handler); - (func)(signal, info, context, &mut FORK_EXECUTOR_GLOBAL_DATA); - } - }, - } - } - - fn signals(&self) -> Vec { - common_signals() - } -} - -#[repr(C)] -#[cfg(all(feature = "std", unix, not(target_os = "linux")))] -struct Timeval { - pub tv_sec: i64, - pub tv_usec: i64, -} - -#[cfg(all(feature = "std", unix, not(target_os = "linux")))] -impl Debug for Timeval { - #[allow(clippy::cast_sign_loss)] - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!( - f, - "Timeval {{ tv_sec: {:?}, tv_usec: {:?} (tv: {:?}) }}", - self.tv_sec, - self.tv_usec, - Duration::new(self.tv_sec as _, (self.tv_usec * 1000) as _) - ) - } -} - -#[repr(C)] -#[cfg(all(feature = "std", unix, not(target_os = "linux")))] -#[derive(Debug)] -struct Itimerval { - pub it_interval: Timeval, - pub it_value: Timeval, -} - -#[cfg(all(feature = "std", unix, not(target_os = "linux")))] -extern "C" { - fn setitimer( - which: libc::c_int, - new_value: *mut Itimerval, - old_value: *mut Itimerval, - ) -> libc::c_int; -} - -#[cfg(all(feature = "std", unix, not(target_os = "linux")))] -const ITIMER_REAL: libc::c_int = 0; - -/// [`InProcessForkExecutor`] is an executor that forks the current process before each execution. -#[cfg(all(feature = "std", unix))] -pub struct InProcessForkExecutor<'a, H, OT, S, SP> -where - H: FnMut(&S::Input) -> ExitKind + ?Sized, - OT: ObserversTuple, - S: UsesInput, - SP: ShMemProvider, -{ - harness_fn: &'a mut H, - shmem_provider: SP, - observers: OT, - handlers: InChildProcessHandlers, - phantom: PhantomData, -} - -/// Timeout executor for [`InProcessForkExecutor`] -#[cfg(all(feature = "std", unix))] -pub struct TimeoutInProcessForkExecutor<'a, H, OT, S, SP> -where - H: FnMut(&S::Input) -> ExitKind + ?Sized, - OT: ObserversTuple, - S: UsesInput, - SP: ShMemProvider, -{ - harness_fn: &'a mut H, - shmem_provider: SP, - observers: OT, - handlers: InChildProcessHandlers, - #[cfg(target_os = "linux")] - itimerspec: libc::itimerspec, - #[cfg(all(unix, not(target_os = "linux")))] - itimerval: Itimerval, - phantom: PhantomData, -} - -#[cfg(all(feature = "std", unix))] -impl<'a, H, OT, S, SP> Debug for InProcessForkExecutor<'a, H, OT, S, SP> -where - H: FnMut(&S::Input) -> ExitKind + ?Sized, - OT: ObserversTuple + Debug, - S: UsesInput, - SP: ShMemProvider, -{ - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.debug_struct("InProcessForkExecutor") - .field("observers", &self.observers) - .field("shmem_provider", &self.shmem_provider) - .finish() - } -} - -#[cfg(all(feature = "std", unix))] -impl<'a, H, OT, S, SP> Debug for TimeoutInProcessForkExecutor<'a, H, OT, S, SP> -where - H: FnMut(&S::Input) -> ExitKind + ?Sized, - OT: ObserversTuple + Debug, - S: UsesInput, - SP: ShMemProvider, -{ - #[cfg(target_os = "linux")] - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.debug_struct("TimeoutInProcessForkExecutor") - .field("observers", &self.observers) - .field("shmem_provider", &self.shmem_provider) - .field("itimerspec", &self.itimerspec) - .finish() - } - - #[cfg(not(target_os = "linux"))] - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - #[cfg(not(target_os = "linux"))] - return f - .debug_struct("TimeoutInProcessForkExecutor") - .field("observers", &self.observers) - .field("shmem_provider", &self.shmem_provider) - .field("itimerval", &self.itimerval) - .finish(); - } -} - -#[cfg(all(feature = "std", unix))] -impl<'a, H, OT, S, SP> UsesState for InProcessForkExecutor<'a, H, OT, S, SP> -where - H: ?Sized + FnMut(&S::Input) -> ExitKind, - OT: ObserversTuple, - S: State, - SP: ShMemProvider, -{ - type State = S; -} - -#[cfg(all(feature = "std", unix))] -impl<'a, H, OT, S, SP> UsesState for TimeoutInProcessForkExecutor<'a, H, OT, S, SP> -where - H: ?Sized + FnMut(&S::Input) -> ExitKind, - OT: ObserversTuple, - S: State, - SP: ShMemProvider, -{ - type State = S; -} - -#[cfg(all(feature = "std", unix))] -impl<'a, EM, H, OT, S, SP, Z> Executor for InProcessForkExecutor<'a, H, OT, S, SP> -where - EM: UsesState, - H: FnMut(&S::Input) -> ExitKind + ?Sized, - OT: ObserversTuple, - S: State + HasExecutions, - SP: ShMemProvider, - Z: UsesState, -{ - #[allow(unreachable_code)] - #[inline] - fn run_target( - &mut self, - _fuzzer: &mut Z, - state: &mut Self::State, - _mgr: &mut EM, - input: &Self::Input, - ) -> Result { - *state.executions_mut() += 1; - unsafe { - self.shmem_provider.pre_fork()?; - match fork() { - Ok(ForkResult::Child) => { - // Child - self.shmem_provider.post_fork(true)?; - - self.handlers.pre_run_target(self, state, input); - - self.observers - .pre_exec_child_all(state, input) - .expect("Failed to run pre_exec on observers"); - - (self.harness_fn)(input); - - self.observers - .post_exec_child_all(state, input, &ExitKind::Ok) - .expect("Failed to run post_exec on observers"); - - libc::_exit(0); - - Ok(ExitKind::Ok) - } - Ok(ForkResult::Parent { child }) => { - // Parent - // log::info!("from parent {} child is {}", std::process::id(), child); - self.shmem_provider.post_fork(false)?; - - let res = waitpid(child, None)?; - - match res { - WaitStatus::Signaled(_, _, _) => Ok(ExitKind::Crash), - WaitStatus::Exited(_, code) => { - if code > 128 && code < 160 { - // Signal exit codes - Ok(ExitKind::Crash) - } else { - Ok(ExitKind::Ok) - } - } - _ => Ok(ExitKind::Ok), - } - } - Err(e) => Err(Error::from(e)), - } - } - } -} - -#[cfg(all(feature = "std", unix))] -impl<'a, EM, H, OT, S, SP, Z> Executor for TimeoutInProcessForkExecutor<'a, H, OT, S, SP> -where - EM: UsesState, - H: FnMut(&S::Input) -> ExitKind + ?Sized, - OT: ObserversTuple, - S: State + HasExecutions, - SP: ShMemProvider, - Z: UsesState, -{ - #[allow(unreachable_code)] - #[inline] - fn run_target( - &mut self, - _fuzzer: &mut Z, - state: &mut Self::State, - _mgr: &mut EM, - input: &Self::Input, - ) -> Result { - *state.executions_mut() += 1; - - unsafe { - self.shmem_provider.pre_fork()?; - match fork() { - Ok(ForkResult::Child) => { - // Child - self.shmem_provider.post_fork(true)?; - - self.handlers.pre_run_target(self, state, input); - - self.observers - .pre_exec_child_all(state, input) - .expect("Failed to run post_exec on observers"); - - #[cfg(target_os = "linux")] - { - let mut timerid: libc::timer_t = null_mut(); - // creates a new per-process interval timer - // we can't do this from the parent, timerid is unique to each process. - libc::timer_create( - libc::CLOCK_MONOTONIC, - null_mut(), - addr_of_mut!(timerid), - ); - - // log::info!("Set timer! {:#?} {timerid:#?}", self.itimerspec); - let _: i32 = libc::timer_settime( - timerid, - 0, - addr_of_mut!(self.itimerspec), - null_mut(), - ); - } - #[cfg(not(target_os = "linux"))] - { - setitimer(ITIMER_REAL, &mut self.itimerval, null_mut()); - } - // log::trace!("{v:#?} {}", nix::errno::errno()); - (self.harness_fn)(input); - - self.observers - .post_exec_child_all(state, input, &ExitKind::Ok) - .expect("Failed to run post_exec on observers"); - - libc::_exit(0); - - Ok(ExitKind::Ok) - } - Ok(ForkResult::Parent { child }) => { - // Parent - // log::trace!("from parent {} child is {}", std::process::id(), child); - self.shmem_provider.post_fork(false)?; - - let res = waitpid(child, None)?; - log::trace!("{res:#?}"); - match res { - WaitStatus::Signaled(_, signal, _) => match signal { - nix::sys::signal::Signal::SIGALRM - | nix::sys::signal::Signal::SIGUSR2 => Ok(ExitKind::Timeout), - _ => Ok(ExitKind::Crash), - }, - WaitStatus::Exited(_, code) => { - if code > 128 && code < 160 { - // Signal exit codes - let signal = code - 128; - if signal == Signal::SigAlarm as libc::c_int - || signal == Signal::SigUser2 as libc::c_int - { - Ok(ExitKind::Timeout) - } else { - Ok(ExitKind::Crash) - } - } else { - Ok(ExitKind::Ok) - } - } - _ => Ok(ExitKind::Ok), - } - } - Err(e) => Err(Error::from(e)), - } - } - } -} - -#[cfg(all(feature = "std", unix))] -impl<'a, H, OT, S, SP> InProcessForkExecutor<'a, H, OT, S, SP> -where - H: FnMut(&S::Input) -> ExitKind + ?Sized, - OT: ObserversTuple, - S: State, - SP: ShMemProvider, -{ - /// Creates a new [`InProcessForkExecutor`] - pub fn new( - harness_fn: &'a mut H, - observers: OT, - _fuzzer: &mut Z, - _state: &mut S, - _event_mgr: &mut EM, - shmem_provider: SP, - ) -> Result - where - EM: EventFirer + EventRestarter, - OF: Feedback, - S: HasSolutions, - Z: HasObjective, - { - let handlers = InChildProcessHandlers::new::()?; - Ok(Self { - harness_fn, - shmem_provider, - observers, - handlers, - phantom: PhantomData, - }) - } - - /// Retrieve the harness function. - #[inline] - pub fn harness(&self) -> &H { - self.harness_fn - } - - /// Retrieve the harness function for a mutable reference. - #[inline] - pub fn harness_mut(&mut self) -> &mut H { - self.harness_fn - } -} - -#[cfg(all(feature = "std", unix))] -impl<'a, H, OT, S, SP> TimeoutInProcessForkExecutor<'a, H, OT, S, SP> -where - H: FnMut(&S::Input) -> ExitKind + ?Sized, - S: State, - OT: ObserversTuple, - SP: ShMemProvider, -{ - /// Creates a new [`TimeoutInProcessForkExecutor`] - #[cfg(target_os = "linux")] - pub fn new( - harness_fn: &'a mut H, - observers: OT, - _fuzzer: &mut Z, - _state: &mut S, - _event_mgr: &mut EM, - timeout: Duration, - shmem_provider: SP, - ) -> Result - where - EM: EventFirer + EventRestarter, - OF: Feedback, - S: HasSolutions, - Z: HasObjective, - { - let handlers = InChildProcessHandlers::with_timeout::()?; - let milli_sec = timeout.as_millis(); - let it_value = libc::timespec { - tv_sec: (milli_sec / 1000) as _, - tv_nsec: ((milli_sec % 1000) * 1000 * 1000) as _, - }; - let it_interval = libc::timespec { - tv_sec: 0, - tv_nsec: 0, - }; - let itimerspec = libc::itimerspec { - it_interval, - it_value, - }; - - Ok(Self { - harness_fn, - shmem_provider, - observers, - handlers, - itimerspec, - phantom: PhantomData, - }) - } - - /// Creates a new [`TimeoutInProcessForkExecutor`], non linux - #[cfg(not(target_os = "linux"))] - pub fn new( - harness_fn: &'a mut H, - observers: OT, - _fuzzer: &mut Z, - _state: &mut S, - _event_mgr: &mut EM, - timeout: Duration, - shmem_provider: SP, - ) -> Result - where - EM: EventFirer + EventRestarter, - OF: Feedback, - S: HasSolutions, - Z: HasObjective, - { - let handlers = InChildProcessHandlers::with_timeout::()?; - let milli_sec = timeout.as_millis(); - let it_value = Timeval { - tv_sec: (milli_sec / 1000) as i64, - tv_usec: (milli_sec % 1000) as i64, - }; - let it_interval = Timeval { - tv_sec: 0, - tv_usec: 0, - }; - let itimerval = Itimerval { - it_interval, - it_value, - }; - - Ok(Self { - harness_fn, - shmem_provider, - observers, - handlers, - itimerval, - phantom: PhantomData, - }) - } - - /// Retrieve the harness function. - #[inline] - pub fn harness(&self) -> &H { - self.harness_fn - } - - /// Retrieve the harness function for a mutable reference. - #[inline] - pub fn harness_mut(&mut self) -> &mut H { - self.harness_fn - } -} - -#[cfg(all(feature = "std", unix))] -impl<'a, H, OT, S, SP> UsesObservers for InProcessForkExecutor<'a, H, OT, S, SP> -where - H: ?Sized + FnMut(&S::Input) -> ExitKind, - OT: ObserversTuple, - S: State, - SP: ShMemProvider, -{ - type Observers = OT; -} - -#[cfg(all(feature = "std", unix))] -impl<'a, H, OT, S, SP> UsesObservers for TimeoutInProcessForkExecutor<'a, H, OT, S, SP> -where - H: ?Sized + FnMut(&S::Input) -> ExitKind, - OT: ObserversTuple, - S: State, - SP: ShMemProvider, -{ - type Observers = OT; -} - -#[cfg(all(feature = "std", unix))] -impl<'a, H, OT, S, SP> HasObservers for InProcessForkExecutor<'a, H, OT, S, SP> -where - H: FnMut(&S::Input) -> ExitKind + ?Sized, - S: State, - OT: ObserversTuple, - SP: ShMemProvider, -{ - #[inline] - fn observers(&self) -> &OT { - &self.observers - } - - #[inline] - fn observers_mut(&mut self) -> &mut OT { - &mut self.observers - } -} - -#[cfg(all(feature = "std", unix))] -impl<'a, H, OT, S, SP> HasObservers for TimeoutInProcessForkExecutor<'a, H, OT, S, SP> -where - H: FnMut(&S::Input) -> ExitKind + ?Sized, - S: State, - OT: ObserversTuple, - SP: ShMemProvider, -{ - #[inline] - fn observers(&self) -> &OT { - &self.observers - } - - #[inline] - fn observers_mut(&mut self) -> &mut OT { - &mut self.observers - } -} - -/// signal handlers and `panic_hooks` for the child process -#[cfg(all(feature = "std", unix))] -pub mod child_signal_handlers { - use alloc::boxed::Box; - use std::panic; - - use libafl_bolts::os::unix_signals::{ucontext_t, Signal}; - use libc::siginfo_t; - - use super::{InProcessForkExecutorGlobalData, FORK_EXECUTOR_GLOBAL_DATA}; - use crate::{ - executors::{ExitKind, HasObservers}, - inputs::UsesInput, - observers::ObserversTuple, - }; - - /// invokes the `post_exec_child` hook on all observer in case the child process panics - pub fn setup_child_panic_hook() - where - E: HasObservers, - { - let old_hook = panic::take_hook(); - panic::set_hook(Box::new(move |panic_info| { - old_hook(panic_info); - let data = unsafe { &mut FORK_EXECUTOR_GLOBAL_DATA }; - if data.is_valid() { - let executor = data.executor_mut::(); - let observers = executor.observers_mut(); - let state = data.state_mut::(); - // Invalidate data to not execute again the observer hooks in the crash handler - let input = data.take_current_input::<::Input>(); - observers - .post_exec_child_all(state, input, &ExitKind::Crash) - .expect("Failed to run post_exec on observers"); - - // std::process::abort(); - unsafe { libc::_exit(128 + 6) }; // ABORT exit code - } - })); - } - - /// invokes the `post_exec` hook on all observer in case the child process crashes - /// - /// # Safety - /// The function should only be called from a child crash handler. - /// It will dereference the `data` pointer and assume it's valid. - #[cfg(unix)] - pub(crate) unsafe fn child_crash_handler( - _signal: Signal, - _info: &mut siginfo_t, - _context: Option<&mut ucontext_t>, - data: &mut InProcessForkExecutorGlobalData, - ) where - E: HasObservers, - { - if data.is_valid() { - let executor = data.executor_mut::(); - let observers = executor.observers_mut(); - let state = data.state_mut::(); - let input = data.take_current_input::<::Input>(); - observers - .post_exec_child_all(state, input, &ExitKind::Crash) - .expect("Failed to run post_exec on observers"); - } - - libc::_exit(128 + (_signal as i32)); - } - - #[cfg(unix)] - pub(crate) unsafe fn child_timeout_handler( - _signal: Signal, - _info: &mut siginfo_t, - _context: Option<&mut ucontext_t>, - data: &mut InProcessForkExecutorGlobalData, - ) where - E: HasObservers, - { - if data.is_valid() { - let executor = data.executor_mut::(); - let observers = executor.observers_mut(); - let state = data.state_mut::(); - let input = data.take_current_input::<::Input>(); - observers - .post_exec_child_all(state, input, &ExitKind::Timeout) - .expect("Failed to run post_exec on observers"); - } - libc::_exit(128 + (_signal as i32)); - } -} - #[cfg(test)] mod tests { - use core::marker::PhantomData; - use libafl_bolts::tuples::tuple_list; - #[cfg(all(feature = "std", feature = "fork", unix))] - use serial_test::serial; use crate::{ + corpus::InMemoryCorpus, events::NopEventManager, - executors::{inprocess::InProcessHandlers, Executor, ExitKind, InProcessExecutor}, - fuzzer::test::NopFuzzer, + executors::{Executor, ExitKind, InProcessExecutor}, + feedbacks::CrashFeedback, inputs::{NopInput, UsesInput}, - state::test::NopState, + schedulers::RandScheduler, + state::StdState, + StdFuzzer, }; impl UsesInput for () { @@ -2197,55 +611,30 @@ mod tests { } #[test] + #[allow(clippy::let_unit_value)] fn test_inmem_exec() { let mut harness = |_buf: &NopInput| ExitKind::Ok; + let rand = libafl_bolts::rands::XkcdRand::new(); + let corpus = InMemoryCorpus::::new(); + let solutions = InMemoryCorpus::new(); + let mut objective = CrashFeedback::new(); + let mut feedback = tuple_list!(); + let sche = RandScheduler::new(); + let mut mgr = NopEventManager::new(); + let mut state = + StdState::new(rand, corpus, solutions, &mut feedback, &mut objective).unwrap(); + let mut fuzzer = StdFuzzer::<_, _, _, ()>::new(sche, feedback, objective); - let mut in_process_executor = InProcessExecutor::<_, _, _> { - harness_fn: &mut harness, - observers: tuple_list!(), - handlers: InProcessHandlers::nop(), - phantom: PhantomData, - }; + let mut in_process_executor = InProcessExecutor::new( + &mut harness, + tuple_list!(), + &mut fuzzer, + &mut state, + &mut mgr, + ) + .unwrap(); let input = NopInput {}; in_process_executor - .run_target( - &mut NopFuzzer::new(), - &mut NopState::new(), - &mut NopEventManager::new(), - &input, - ) - .unwrap(); - } - - #[test] - #[serial] - #[cfg_attr(miri, ignore)] - #[cfg(all(feature = "std", feature = "fork", unix))] - fn test_inprocessfork_exec() { - use libafl_bolts::shmem::{ShMemProvider, StdShMemProvider}; - - use crate::{ - events::SimpleEventManager, - executors::{inprocess::InChildProcessHandlers, InProcessForkExecutor}, - fuzzer::test::NopFuzzer, - state::test::NopState, - }; - - let provider = StdShMemProvider::new().unwrap(); - - let mut harness = |_buf: &NopInput| ExitKind::Ok; - let mut in_process_fork_executor = InProcessForkExecutor::<_, (), _, _> { - harness_fn: &mut harness, - shmem_provider: provider, - observers: tuple_list!(), - handlers: InChildProcessHandlers::nop(), - phantom: PhantomData, - }; - let input = NopInput {}; - let mut fuzzer = NopFuzzer::new(); - let mut state = NopState::new(); - let mut mgr = SimpleEventManager::printing(); - in_process_fork_executor .run_target(&mut fuzzer, &mut state, &mut mgr, &input) .unwrap(); } @@ -2258,6 +647,7 @@ mod tests { pub mod pybind { use alloc::boxed::Box; + use libafl_bolts::tuples::tuple_list; use pyo3::{prelude::*, types::PyBytes}; use crate::{ @@ -2288,7 +678,8 @@ pub mod pybind { py_event_manager: &mut PythonEventManager, ) -> Self { Self { - inner: OwnedInProcessExecutor::new( + inner: OwnedInProcessExecutor::generic( + tuple_list!(), Box::new(move |input: &BytesInput| { Python::with_gil(|py| -> PyResult<()> { let args = (PyBytes::new(py, input.bytes()),); diff --git a/libafl/src/executors/inprocess_fork.rs b/libafl/src/executors/inprocess_fork.rs new file mode 100644 index 0000000000..e48c00d3dd --- /dev/null +++ b/libafl/src/executors/inprocess_fork.rs @@ -0,0 +1,613 @@ +//! The `GenericInProcessForkExecutor` to do forking before executing the harness in-processly +#[cfg(target_os = "linux")] +use core::ptr::addr_of_mut; +use core::{ + ffi::c_void, + fmt::{self, Debug, Formatter}, + marker::PhantomData, + ptr::{null_mut, write_volatile}, + sync::atomic::{compiler_fence, Ordering}, + time::Duration, +}; + +use libafl_bolts::{ + os::unix_signals::{ucontext_t, Signal}, + shmem::ShMemProvider, + tuples::{tuple_list, Merge}, +}; +use libc::siginfo_t; +use nix::{ + sys::wait::{waitpid, WaitStatus}, + unistd::{fork, ForkResult}, +}; + +use super::hooks::ExecutorHooksTuple; +use crate::{ + events::{EventFirer, EventRestarter}, + executors::{ + hooks::inprocess_fork::{ + InChildProcessHooks, InProcessForkExecutorGlobalData, FORK_EXECUTOR_GLOBAL_DATA, + }, + Executor, ExitKind, HasObservers, + }, + feedbacks::Feedback, + fuzzer::HasObjective, + inputs::UsesInput, + observers::{ObserversTuple, UsesObservers}, + state::{HasExecutions, HasSolutions, State, UsesState}, + Error, +}; +/// The signature of the crash handler function +pub(crate) type ForkHandlerFuncPtr = unsafe fn( + Signal, + &mut siginfo_t, + Option<&mut ucontext_t>, + data: &mut InProcessForkExecutorGlobalData, +); + +#[cfg(all(unix, not(target_os = "linux")))] +use crate::executors::hooks::timer::{setitimer, Itimerval, Timeval, ITIMER_REAL}; + +/// The `InProcessForkExecutor` with no user hooks +pub type InProcessForkExecutor<'a, H, OT, S, SP> = + GenericInProcessForkExecutor<'a, H, (), OT, S, SP>; + +impl<'a, H, OT, S, SP> InProcessForkExecutor<'a, H, OT, S, SP> +where + H: FnMut(&S::Input) -> ExitKind + ?Sized, + S: State, + OT: ObserversTuple, + SP: ShMemProvider, +{ + #[allow(clippy::too_many_arguments)] + /// The constructor for `InProcessForkExecutor` + pub fn new( + harness_fn: &'a mut H, + observers: OT, + fuzzer: &mut Z, + state: &mut S, + event_mgr: &mut EM, + timeout: Duration, + shmem_provider: SP, + ) -> Result + where + EM: EventFirer + EventRestarter, + OF: Feedback, + S: HasSolutions, + Z: HasObjective, + { + Self::with_hooks( + tuple_list!(), + harness_fn, + observers, + fuzzer, + state, + event_mgr, + timeout, + shmem_provider, + ) + } +} + +/// [`GenericInProcessForkExecutor`] is an executor that forks the current process before each execution. +pub struct GenericInProcessForkExecutor<'a, H, HT, OT, S, SP> +where + H: FnMut(&S::Input) -> ExitKind + ?Sized, + OT: ObserversTuple, + S: UsesInput, + SP: ShMemProvider, + HT: ExecutorHooksTuple, +{ + hooks: (InChildProcessHooks, HT), + harness_fn: &'a mut H, + shmem_provider: SP, + observers: OT, + #[cfg(target_os = "linux")] + itimerspec: libc::itimerspec, + #[cfg(all(unix, not(target_os = "linux")))] + itimerval: Itimerval, + phantom: PhantomData, +} + +impl<'a, H, HT, OT, S, SP> Debug for GenericInProcessForkExecutor<'a, H, HT, OT, S, SP> +where + H: FnMut(&S::Input) -> ExitKind + ?Sized, + OT: ObserversTuple + Debug, + S: UsesInput, + SP: ShMemProvider, + HT: ExecutorHooksTuple, +{ + #[cfg(target_os = "linux")] + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("GenericInProcessForkExecutor") + .field("observers", &self.observers) + .field("shmem_provider", &self.shmem_provider) + .field("itimerspec", &self.itimerspec) + .finish() + } + + #[cfg(not(target_os = "linux"))] + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + #[cfg(not(target_os = "linux"))] + return f + .debug_struct("GenericInProcessForkExecutor") + .field("observers", &self.observers) + .field("shmem_provider", &self.shmem_provider) + .field("itimerval", &self.itimerval) + .finish(); + } +} + +impl<'a, H, HT, OT, S, SP> UsesState for GenericInProcessForkExecutor<'a, H, HT, OT, S, SP> +where + H: ?Sized + FnMut(&S::Input) -> ExitKind, + OT: ObserversTuple, + S: State, + SP: ShMemProvider, + HT: ExecutorHooksTuple, +{ + type State = S; +} + +impl<'a, EM, H, HT, OT, S, SP, Z> Executor + for GenericInProcessForkExecutor<'a, H, HT, OT, S, SP> +where + EM: UsesState, + H: FnMut(&S::Input) -> ExitKind + ?Sized, + OT: ObserversTuple, + S: State + HasExecutions, + SP: ShMemProvider, + HT: ExecutorHooksTuple, + Z: UsesState, +{ + #[allow(unreachable_code)] + #[inline] + fn run_target( + &mut self, + fuzzer: &mut Z, + state: &mut Self::State, + mgr: &mut EM, + input: &Self::Input, + ) -> Result { + *state.executions_mut() += 1; + + unsafe { + self.shmem_provider.pre_fork()?; + match fork() { + Ok(ForkResult::Child) => { + // Child + self.shmem_provider.post_fork(true)?; + + self.enter_target(fuzzer, state, mgr, input); + self.hooks.pre_exec_all(fuzzer, state, mgr, input); + + self.observers + .pre_exec_child_all(state, input) + .expect("Failed to run post_exec on observers"); + + #[cfg(target_os = "linux")] + { + let mut timerid: libc::timer_t = null_mut(); + // creates a new per-process interval timer + // we can't do this from the parent, timerid is unique to each process. + libc::timer_create( + libc::CLOCK_MONOTONIC, + null_mut(), + addr_of_mut!(timerid), + ); + + // log::info!("Set timer! {:#?} {timerid:#?}", self.itimerspec); + let _: i32 = libc::timer_settime( + timerid, + 0, + addr_of_mut!(self.itimerspec), + null_mut(), + ); + } + #[cfg(not(target_os = "linux"))] + { + setitimer(ITIMER_REAL, &mut self.itimerval, null_mut()); + } + // log::trace!("{v:#?} {}", nix::errno::errno()); + (self.harness_fn)(input); + + self.observers + .post_exec_child_all(state, input, &ExitKind::Ok) + .expect("Failed to run post_exec on observers"); + + self.hooks.post_exec_all(fuzzer, state, mgr, input); + self.leave_target(fuzzer, state, mgr, input); + + libc::_exit(0); + + Ok(ExitKind::Ok) + } + Ok(ForkResult::Parent { child }) => { + // Parent + // log::trace!("from parent {} child is {}", std::process::id(), child); + self.shmem_provider.post_fork(false)?; + + let res = waitpid(child, None)?; + log::trace!("{res:#?}"); + match res { + WaitStatus::Signaled(_, signal, _) => match signal { + nix::sys::signal::Signal::SIGALRM + | nix::sys::signal::Signal::SIGUSR2 => Ok(ExitKind::Timeout), + _ => Ok(ExitKind::Crash), + }, + WaitStatus::Exited(_, code) => { + if code > 128 && code < 160 { + // Signal exit codes + let signal = code - 128; + if signal == Signal::SigAlarm as libc::c_int + || signal == Signal::SigUser2 as libc::c_int + { + Ok(ExitKind::Timeout) + } else { + Ok(ExitKind::Crash) + } + } else { + Ok(ExitKind::Ok) + } + } + _ => Ok(ExitKind::Ok), + } + } + Err(e) => Err(Error::from(e)), + } + } + } +} + +impl<'a, H, HT, OT, S, SP> GenericInProcessForkExecutor<'a, H, HT, OT, S, SP> +where + H: FnMut(&S::Input) -> ExitKind + ?Sized, + HT: ExecutorHooksTuple, + S: State, + OT: ObserversTuple, + SP: ShMemProvider, +{ + #[inline] + /// This function marks the boundary between the fuzzer and the target. + pub fn enter_target( + &mut self, + _fuzzer: &mut Z, + state: &mut ::State, + _event_mgr: &mut EM, + input: &::Input, + ) { + unsafe { + let data = &mut FORK_EXECUTOR_GLOBAL_DATA; + write_volatile(&mut data.executor_ptr, self as *const _ as *const c_void); + write_volatile( + &mut data.current_input_ptr, + input as *const _ as *const c_void, + ); + write_volatile(&mut data.state_ptr, state as *mut _ as *mut c_void); + compiler_fence(Ordering::SeqCst); + } + } + + #[inline] + /// This function marks the boundary between the fuzzer and the target. + pub fn leave_target( + &mut self, + _fuzzer: &mut Z, + _state: &mut ::State, + _event_mgr: &mut EM, + _input: &::Input, + ) { + // do nothing + } + + /// Creates a new [`GenericInProcessForkExecutor`] with custom hooks + #[cfg(target_os = "linux")] + #[allow(clippy::too_many_arguments)] + pub fn with_hooks( + userhooks: HT, + harness_fn: &'a mut H, + observers: OT, + _fuzzer: &mut Z, + state: &mut S, + _event_mgr: &mut EM, + timeout: Duration, + shmem_provider: SP, + ) -> Result + where + EM: EventFirer + EventRestarter, + OF: Feedback, + S: HasSolutions, + Z: HasObjective, + { + let default_hooks = InChildProcessHooks::new::()?; + let mut hooks = tuple_list!(default_hooks).merge(userhooks); + hooks.init_all::(state); + + let milli_sec = timeout.as_millis(); + let it_value = libc::timespec { + tv_sec: (milli_sec / 1000) as _, + tv_nsec: ((milli_sec % 1000) * 1000 * 1000) as _, + }; + let it_interval = libc::timespec { + tv_sec: 0, + tv_nsec: 0, + }; + let itimerspec = libc::itimerspec { + it_interval, + it_value, + }; + + Ok(Self { + harness_fn, + shmem_provider, + observers, + hooks, + itimerspec, + phantom: PhantomData, + }) + } + + /// Creates a new [`GenericInProcessForkExecutor`], non linux + #[cfg(not(target_os = "linux"))] + #[allow(clippy::too_many_arguments)] + pub fn with_hooks( + userhooks: HT, + harness_fn: &'a mut H, + observers: OT, + _fuzzer: &mut Z, + state: &mut S, + _event_mgr: &mut EM, + timeout: Duration, + shmem_provider: SP, + ) -> Result + where + EM: EventFirer + EventRestarter, + OF: Feedback, + S: HasSolutions, + Z: HasObjective, + { + let default_hooks = InChildProcessHooks::new::()?; + let mut hooks = tuple_list!(default_hooks).merge(userhooks); + hooks.init_all::(state); + + let milli_sec = timeout.as_millis(); + let it_value = Timeval { + tv_sec: (milli_sec / 1000) as i64, + tv_usec: (milli_sec % 1000) as i64, + }; + let it_interval = Timeval { + tv_sec: 0, + tv_usec: 0, + }; + let itimerval = Itimerval { + it_interval, + it_value, + }; + + Ok(Self { + harness_fn, + shmem_provider, + observers, + hooks, + itimerval, + phantom: PhantomData, + }) + } + + /// Retrieve the harness function. + #[inline] + pub fn harness(&self) -> &H { + self.harness_fn + } + + /// Retrieve the harness function for a mutable reference. + #[inline] + pub fn harness_mut(&mut self) -> &mut H { + self.harness_fn + } +} + +impl<'a, H, HT, OT, S, SP> UsesObservers for GenericInProcessForkExecutor<'a, H, HT, OT, S, SP> +where + H: ?Sized + FnMut(&S::Input) -> ExitKind, + HT: ExecutorHooksTuple, + OT: ObserversTuple, + S: State, + SP: ShMemProvider, +{ + type Observers = OT; +} + +impl<'a, H, HT, OT, S, SP> HasObservers for GenericInProcessForkExecutor<'a, H, HT, OT, S, SP> +where + H: FnMut(&S::Input) -> ExitKind + ?Sized, + HT: ExecutorHooksTuple, + S: State, + OT: ObserversTuple, + SP: ShMemProvider, +{ + #[inline] + fn observers(&self) -> &OT { + &self.observers + } + + #[inline] + fn observers_mut(&mut self) -> &mut OT { + &mut self.observers + } +} +/// signal hooks and `panic_hooks` for the child process + +pub mod child_signal_handlers { + use alloc::boxed::Box; + use std::panic; + + use libafl_bolts::os::unix_signals::{ucontext_t, Signal}; + use libc::siginfo_t; + + use crate::{ + executors::{ + hooks::inprocess_fork::{InProcessForkExecutorGlobalData, FORK_EXECUTOR_GLOBAL_DATA}, + ExitKind, HasObservers, + }, + inputs::UsesInput, + observers::ObserversTuple, + }; + + /// invokes the `post_exec_child` hook on all observer in case the child process panics + pub fn setup_child_panic_hook() + where + E: HasObservers, + { + let old_hook = panic::take_hook(); + panic::set_hook(Box::new(move |panic_info| { + old_hook(panic_info); + let data = unsafe { &mut FORK_EXECUTOR_GLOBAL_DATA }; + if data.is_valid() { + let executor = data.executor_mut::(); + let observers = executor.observers_mut(); + let state = data.state_mut::(); + // Invalidate data to not execute again the observer hooks in the crash handler + let input = data.take_current_input::<::Input>(); + observers + .post_exec_child_all(state, input, &ExitKind::Crash) + .expect("Failed to run post_exec on observers"); + + // std::process::abort(); + unsafe { libc::_exit(128 + 6) }; // ABORT exit code + } + })); + } + + /// invokes the `post_exec` hook on all observer in case the child process crashes + /// + /// # Safety + /// The function should only be called from a child crash handler. + /// It will dereference the `data` pointer and assume it's valid. + #[cfg(unix)] + #[allow(clippy::needless_pass_by_value)] + pub(crate) unsafe fn child_crash_handler( + _signal: Signal, + _info: &mut siginfo_t, + _context: Option<&mut ucontext_t>, + data: &mut InProcessForkExecutorGlobalData, + ) where + E: HasObservers, + { + if data.is_valid() { + let executor = data.executor_mut::(); + let observers = executor.observers_mut(); + let state = data.state_mut::(); + let input = data.take_current_input::<::Input>(); + observers + .post_exec_child_all(state, input, &ExitKind::Crash) + .expect("Failed to run post_exec on observers"); + } + + libc::_exit(128 + (_signal as i32)); + } + + #[cfg(unix)] + #[allow(clippy::needless_pass_by_value)] + pub(crate) unsafe fn child_timeout_handler( + _signal: Signal, + _info: &mut siginfo_t, + _context: Option<&mut ucontext_t>, + data: &mut InProcessForkExecutorGlobalData, + ) where + E: HasObservers, + { + if data.is_valid() { + let executor = data.executor_mut::(); + let observers = executor.observers_mut(); + let state = data.state_mut::(); + let input = data.take_current_input::<::Input>(); + observers + .post_exec_child_all(state, input, &ExitKind::Timeout) + .expect("Failed to run post_exec on observers"); + } + libc::_exit(128 + (_signal as i32)); + } +} + +#[cfg(test)] +mod tests { + use libafl_bolts::tuples::tuple_list; + + use crate::{executors::ExitKind, inputs::NopInput}; + + #[test] + #[cfg_attr(miri, ignore)] + #[cfg(all(feature = "std", feature = "fork", unix))] + fn test_inprocessfork_exec() { + use core::marker::PhantomData; + + use libafl_bolts::shmem::{ShMemProvider, StdShMemProvider}; + #[cfg(target_os = "linux")] + use libc::{itimerspec, timespec}; + + #[cfg(not(target_os = "linux"))] + use crate::executors::hooks::timer::{Itimerval, Timeval}; + use crate::{ + events::SimpleEventManager, + executors::{ + hooks::inprocess_fork::InChildProcessHooks, + inprocess_fork::GenericInProcessForkExecutor, Executor, + }, + fuzzer::test::NopFuzzer, + state::test::NopState, + }; + + let provider = StdShMemProvider::new().unwrap(); + + #[cfg(target_os = "linux")] + let timespec = timespec { + tv_sec: 5, + tv_nsec: 0, + }; + #[cfg(target_os = "linux")] + let itimerspec = itimerspec { + it_interval: timespec, + it_value: timespec, + }; + + #[cfg(not(target_os = "linux"))] + let timespec = Timeval { + tv_sec: 5, + tv_usec: 0, + }; + #[cfg(not(target_os = "linux"))] + let itimerspec = Itimerval { + it_interval: timespec, + it_value: timespec, + }; + + let mut harness = |_buf: &NopInput| ExitKind::Ok; + let default = InChildProcessHooks::nop(); + #[cfg(target_os = "linux")] + let mut in_process_fork_executor = GenericInProcessForkExecutor::<_, (), (), _, _> { + hooks: tuple_list!(default), + harness_fn: &mut harness, + shmem_provider: provider, + observers: tuple_list!(), + itimerspec, + phantom: PhantomData, + }; + #[cfg(not(target_os = "linux"))] + let mut in_process_fork_executor = GenericInProcessForkExecutor::<_, (), (), _, _> { + harness_fn: &mut harness, + shmem_provider: provider, + observers: tuple_list!(), + hooks: tuple_list!(default), + itimerval: itimerspec, + phantom: PhantomData, + }; + let input = NopInput {}; + let mut fuzzer = NopFuzzer::new(); + let mut state = NopState::new(); + let mut mgr = SimpleEventManager::printing(); + in_process_fork_executor + .run_target(&mut fuzzer, &mut state, &mut mgr, &input) + .unwrap(); + } +} diff --git a/libafl/src/executors/mod.rs b/libafl/src/executors/mod.rs index 699c5654b9..14bb785540 100644 --- a/libafl/src/executors/mod.rs +++ b/libafl/src/executors/mod.rs @@ -1,5 +1,7 @@ //! Executors take input, and run it in the target. +#[cfg(unix)] +use alloc::vec::Vec; use core::fmt::Debug; pub use combined::CombinedExecutor; @@ -10,11 +12,11 @@ pub use differential::DiffExecutor; pub use forkserver::{Forkserver, ForkserverExecutor, TimeoutForkserverExecutor}; pub use inprocess::InProcessExecutor; #[cfg(all(feature = "std", feature = "fork", unix))] -pub use inprocess::InProcessForkExecutor; +pub use inprocess_fork::InProcessForkExecutor; +#[cfg(unix)] +use libafl_bolts::os::unix_signals::Signal; use serde::{Deserialize, Serialize}; pub use shadow::ShadowExecutor; -#[cfg(any(unix, feature = "std"))] -pub use timeout::TimeoutExecutor; pub use with_observers::WithObservers; use crate::{ @@ -30,13 +32,18 @@ pub mod differential; #[cfg(all(feature = "std", feature = "fork", unix))] pub mod forkserver; pub mod inprocess; + +/// The module for inproc fork executor +#[cfg(all(feature = "std", unix))] +pub mod inprocess_fork; + pub mod shadow; -/// Timeout executor. -/// Not possible on `no-std` Windows or `no-std`, but works for unix -#[cfg(any(unix, feature = "std"))] -pub mod timeout; + pub mod with_observers; +/// The module for all the hooks +pub mod hooks; + /// How an execution finished. #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] #[cfg_attr( @@ -135,10 +142,25 @@ where { WithObservers::new(self, observers) } +} - /// Custom Reset Handler, e.g., to reset timers - #[inline] - fn post_run_reset(&mut self) {} +/// The common signals we want to handle +#[cfg(unix)] +#[inline] +#[must_use] +pub fn common_signals() -> Vec { + vec![ + Signal::SigAlarm, + Signal::SigUser2, + Signal::SigAbort, + Signal::SigBus, + #[cfg(feature = "handle_sigpipe")] + Signal::SigPipe, + Signal::SigFloatingPointException, + Signal::SigIllegalInstruction, + Signal::SigSegmentationFault, + Signal::SigTrap, + ] } #[cfg(test)] diff --git a/libafl/src/executors/shadow.rs b/libafl/src/executors/shadow.rs index 94a73a9f86..f2b30c0fec 100644 --- a/libafl/src/executors/shadow.rs +++ b/libafl/src/executors/shadow.rs @@ -70,9 +70,7 @@ where mgr: &mut EM, input: &Self::Input, ) -> Result { - let ret = self.executor.run_target(fuzzer, state, mgr, input); - self.executor.post_run_reset(); - ret + self.executor.run_target(fuzzer, state, mgr, input) } } diff --git a/libafl/src/executors/timeout.rs b/libafl/src/executors/timeout.rs deleted file mode 100644 index be10241473..0000000000 --- a/libafl/src/executors/timeout.rs +++ /dev/null @@ -1,583 +0,0 @@ -//! A `TimeoutExecutor` sets a timeout before each target run - -#[cfg(target_os = "linux")] -use core::ptr::{addr_of, addr_of_mut}; -#[cfg(any(windows, target_os = "linux"))] -use core::{ffi::c_void, ptr::write_volatile}; -#[cfg(any(windows, unix))] -use core::{ - fmt::{self, Debug, Formatter}, - time::Duration, -}; -#[cfg(unix)] -use core::{mem::zeroed, ptr::null_mut}; -#[cfg(windows)] -use core::{ - ptr::addr_of_mut, - sync::atomic::{compiler_fence, Ordering}, -}; - -#[cfg(target_os = "linux")] -use libafl_bolts::current_time; -#[cfg(all(unix, not(target_os = "linux")))] -use libc::c_int; -#[cfg(all(windows, feature = "std"))] -use windows::Win32::{ - Foundation::FILETIME, - System::Threading::{ - CreateThreadpoolTimer, EnterCriticalSection, InitializeCriticalSection, - LeaveCriticalSection, SetThreadpoolTimer, CRITICAL_SECTION, PTP_CALLBACK_INSTANCE, - PTP_TIMER, TP_CALLBACK_ENVIRON_V3, - }, -}; - -#[cfg(all(windows, feature = "std"))] -use crate::executors::inprocess::HasInProcessHandlers; -#[cfg(any(windows, target_os = "linux"))] -use crate::executors::inprocess::GLOBAL_STATE; -use crate::{ - executors::{inprocess::InProcessExecutorHandlerData, Executor, ExitKind, HasObservers}, - observers::UsesObservers, - state::UsesState, - Error, -}; - -#[repr(C)] -#[cfg(all(unix, not(target_os = "linux")))] -pub(crate) struct Timeval { - pub tv_sec: i64, - pub tv_usec: i64, -} - -#[cfg(all(unix, not(target_os = "linux")))] -impl Debug for Timeval { - #[allow(clippy::cast_sign_loss)] - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!( - f, - "Timeval {{ tv_sec: {:?}, tv_usec: {:?} (tv: {:?}) }}", - self.tv_sec, - self.tv_usec, - Duration::new(self.tv_sec as _, (self.tv_usec * 1000) as _) - ) - } -} - -#[repr(C)] -#[cfg(all(unix, not(target_os = "linux")))] -#[derive(Debug)] -pub(crate) struct Itimerval { - pub it_interval: Timeval, - pub it_value: Timeval, -} - -#[cfg(all(unix, not(target_os = "linux")))] -extern "C" { - fn setitimer(which: c_int, new_value: *mut Itimerval, old_value: *mut Itimerval) -> c_int; -} - -#[cfg(all(unix, not(target_os = "linux")))] -const ITIMER_REAL: c_int = 0; - -/// The timeout executor is a wrapper that sets a timeout before each run -pub struct TimeoutExecutor { - /// The wrapped [`Executor`] - executor: E, - #[cfg(target_os = "linux")] - itimerspec: libc::itimerspec, - #[cfg(target_os = "linux")] - timerid: libc::timer_t, - #[cfg(all(unix, not(target_os = "linux")))] - itimerval: Itimerval, - #[cfg(windows)] - milli_sec: i64, - #[cfg(windows)] - ptp_timer: PTP_TIMER, - #[cfg(windows)] - critical: CRITICAL_SECTION, - - exec_tmout: Duration, - - // for batch mode (linux only atm) - #[allow(unused)] - batch_mode: bool, - #[allow(unused)] - executions: u32, - #[allow(unused)] - avg_mul_k: u32, - #[allow(unused)] - last_signal_time: Duration, - #[allow(unused)] - avg_exec_time: Duration, - #[allow(unused)] - start_time: Duration, - #[allow(unused)] - tmout_start_time: Duration, -} - -impl Debug for TimeoutExecutor { - #[cfg(windows)] - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.debug_struct("TimeoutExecutor") - .field("executor", &self.executor) - .field("milli_sec", &self.milli_sec) - .finish_non_exhaustive() - } - - #[cfg(target_os = "linux")] - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.debug_struct("TimeoutExecutor") - .field("executor", &self.executor) - .field( - "milli_sec", - &(&self.itimerspec.it_value.tv_sec * 1000 - + &self.itimerspec.it_value.tv_nsec / 1000 / 1000), - ) - .finish_non_exhaustive() - } - - #[cfg(all(unix, not(target_os = "linux")))] - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.debug_struct("TimeoutExecutor") - .field("executor", &self.executor) - .field("itimerval", &self.itimerval) - .finish_non_exhaustive() - } -} - -#[cfg(windows)] -#[allow(non_camel_case_types)] -type PTP_TIMER_CALLBACK = unsafe extern "system" fn( - param0: PTP_CALLBACK_INSTANCE, - param1: *mut c_void, - param2: PTP_TIMER, -); - -#[cfg(target_os = "linux")] -impl TimeoutExecutor { - /// Create a new [`TimeoutExecutor`], wrapping the given `executor` and checking for timeouts. - /// This should usually be used for `InProcess` fuzzing. - pub fn new(executor: E, exec_tmout: Duration) -> Self { - let milli_sec = exec_tmout.as_millis(); - let it_value = libc::timespec { - tv_sec: (milli_sec / 1000) as _, - tv_nsec: ((milli_sec % 1000) * 1000 * 1000) as _, - }; - let it_interval = libc::timespec { - tv_sec: 0, - tv_nsec: 0, - }; - let itimerspec = libc::itimerspec { - it_interval, - it_value, - }; - let mut timerid: libc::timer_t = null_mut(); - unsafe { - // creates a new per-process interval timer - libc::timer_create(libc::CLOCK_MONOTONIC, null_mut(), addr_of_mut!(timerid)); - } - Self { - executor, - itimerspec, - timerid, - exec_tmout, - batch_mode: false, - executions: 0, - avg_mul_k: 1, - last_signal_time: Duration::ZERO, - avg_exec_time: Duration::ZERO, - start_time: Duration::ZERO, - tmout_start_time: Duration::ZERO, - } - } - - /// Create a new [`TimeoutExecutor`], wrapping the given `executor` and checking for timeouts. - /// With this method batch mode is enabled. - pub fn batch_mode(executor: E, exec_tmout: Duration) -> Self { - let mut me = Self::new(executor, exec_tmout); - me.batch_mode = true; - me - } - - /// Set the timeout for this executor - pub fn set_timeout(&mut self, exec_tmout: Duration) { - let milli_sec = exec_tmout.as_millis(); - let it_value = libc::timespec { - tv_sec: (milli_sec / 1000) as _, - tv_nsec: ((milli_sec % 1000) * 1000 * 1000) as _, - }; - let it_interval = libc::timespec { - tv_sec: 0, - tv_nsec: 0, - }; - let itimerspec = libc::itimerspec { - it_interval, - it_value, - }; - self.itimerspec = itimerspec; - self.exec_tmout = exec_tmout; - } - - pub(crate) fn handle_timeout(&mut self, data: &InProcessExecutorHandlerData) -> bool { - if !self.batch_mode { - return false; - } - //eprintln!("handle_timeout {:?} {}", self.avg_exec_time, self.avg_mul_k); - let cur_time = current_time(); - if !data.is_valid() { - // outside the target - unsafe { - let disarmed: libc::itimerspec = zeroed(); - libc::timer_settime(self.timerid, 0, addr_of!(disarmed), null_mut()); - } - let elapsed = cur_time - self.tmout_start_time; - // set timer the next exec - if self.executions > 0 { - self.avg_exec_time = elapsed / self.executions; - self.executions = 0; - } - self.avg_mul_k += 1; - self.last_signal_time = cur_time; - return true; - } - - let elapsed_run = cur_time - self.start_time; - if elapsed_run < self.exec_tmout { - // fp, reset timeout - unsafe { - libc::timer_settime(self.timerid, 0, addr_of!(self.itimerspec), null_mut()); - } - if self.executions > 0 { - let elapsed = cur_time - self.tmout_start_time; - self.avg_exec_time = elapsed / self.executions; - self.executions = 0; // It will be 1 when the exec finish - } - self.tmout_start_time = current_time(); - self.avg_mul_k += 1; - self.last_signal_time = cur_time; - true - } else { - false - } - } -} - -#[cfg(all(unix, not(target_os = "linux")))] -impl TimeoutExecutor { - /// Create a new [`TimeoutExecutor`], wrapping the given `executor` and checking for timeouts. - /// This should usually be used for `InProcess` fuzzing. - pub fn new(executor: E, exec_tmout: Duration) -> Self { - let milli_sec = exec_tmout.as_millis(); - let it_value = Timeval { - tv_sec: (milli_sec / 1000) as i64, - tv_usec: (milli_sec % 1000) as i64, - }; - let it_interval = Timeval { - tv_sec: 0, - tv_usec: 0, - }; - let itimerval = Itimerval { - it_interval, - it_value, - }; - Self { - executor, - itimerval, - exec_tmout, - batch_mode: false, - executions: 0, - avg_mul_k: 1, - last_signal_time: Duration::ZERO, - avg_exec_time: Duration::ZERO, - start_time: Duration::ZERO, - tmout_start_time: Duration::ZERO, - } - } - - /// Set the timeout for this executor - pub fn set_timeout(&mut self, exec_tmout: Duration) { - let milli_sec = exec_tmout.as_millis(); - let it_value = Timeval { - tv_sec: (milli_sec / 1000) as i64, - tv_usec: (milli_sec % 1000) as i64, - }; - let it_interval = Timeval { - tv_sec: 0, - tv_usec: 0, - }; - let itimerval = Itimerval { - it_interval, - it_value, - }; - self.itimerval = itimerval; - self.exec_tmout = exec_tmout; - } - - #[allow(clippy::unused_self)] - pub(crate) fn handle_timeout(&mut self, _data: &mut InProcessExecutorHandlerData) -> bool { - false // TODO - } -} - -#[cfg(windows)] -impl TimeoutExecutor { - /// Create a new [`TimeoutExecutor`], wrapping the given `executor` and checking for timeouts. - pub fn new(executor: E, exec_tmout: Duration) -> Self { - let milli_sec = exec_tmout.as_millis() as i64; - let timeout_handler: PTP_TIMER_CALLBACK = - unsafe { std::mem::transmute(executor.inprocess_handlers().timeout_handler) }; - let ptp_timer = unsafe { - CreateThreadpoolTimer( - Some(timeout_handler), - Some(addr_of_mut!(GLOBAL_STATE) as *mut c_void), - Some(&TP_CALLBACK_ENVIRON_V3::default()), - ) - } - .expect("CreateThreadpoolTimer failed!"); - let mut critical = CRITICAL_SECTION::default(); - - unsafe { - InitializeCriticalSection(&mut critical); - } - - Self { - executor, - milli_sec, - ptp_timer, - critical, - exec_tmout, - batch_mode: false, - executions: 0, - avg_mul_k: 1, - last_signal_time: Duration::ZERO, - avg_exec_time: Duration::ZERO, - start_time: Duration::ZERO, - tmout_start_time: Duration::ZERO, - } - } - - /// Set the timeout for this executor - pub fn set_timeout(&mut self, exec_tmout: Duration) { - self.milli_sec = exec_tmout.as_millis() as i64; - self.exec_tmout = exec_tmout; - } - - #[allow(clippy::unused_self)] - pub(crate) fn handle_timeout(&mut self, _data: &mut InProcessExecutorHandlerData) -> bool { - false // TODO - } - - /// Retrieve the inner `Executor` that is wrapped by this `TimeoutExecutor`. - pub fn inner(&mut self) -> &mut E { - &mut self.executor - } -} - -#[cfg(windows)] -impl Executor for TimeoutExecutor -where - E: Executor + HasInProcessHandlers, - EM: UsesState, - Z: UsesState, -{ - #[allow(clippy::cast_sign_loss)] - fn run_target( - &mut self, - fuzzer: &mut Z, - state: &mut Self::State, - mgr: &mut EM, - input: &Self::Input, - ) -> Result { - unsafe { - let data = &mut GLOBAL_STATE; - write_volatile( - &mut data.timeout_executor_ptr, - self as *mut _ as *mut c_void, - ); - - write_volatile(&mut data.ptp_timer, Some(self.ptp_timer)); - write_volatile( - &mut data.critical, - addr_of_mut!(self.critical) as *mut c_void, - ); - write_volatile( - &mut data.timeout_input_ptr, - addr_of_mut!(data.current_input_ptr) as *mut c_void, - ); - let tm: i64 = -self.milli_sec * 10 * 1000; - let ft = FILETIME { - dwLowDateTime: (tm & 0xffffffff) as u32, - dwHighDateTime: (tm >> 32) as u32, - }; - - compiler_fence(Ordering::SeqCst); - EnterCriticalSection(&mut self.critical); - compiler_fence(Ordering::SeqCst); - data.in_target = 1; - compiler_fence(Ordering::SeqCst); - LeaveCriticalSection(&mut self.critical); - compiler_fence(Ordering::SeqCst); - - SetThreadpoolTimer(self.ptp_timer, Some(&ft), 0, 0); - - let ret = self.executor.run_target(fuzzer, state, mgr, input); - - compiler_fence(Ordering::SeqCst); - EnterCriticalSection(&mut self.critical); - compiler_fence(Ordering::SeqCst); - // Timeout handler will do nothing after we increment in_target value. - data.in_target = 0; - compiler_fence(Ordering::SeqCst); - LeaveCriticalSection(&mut self.critical); - compiler_fence(Ordering::SeqCst); - - write_volatile(&mut data.timeout_input_ptr, core::ptr::null_mut()); - - self.post_run_reset(); - ret - } - } - - /// Deletes this timer queue - /// # Safety - /// Will dereference the given `tp_timer` pointer, unchecked. - fn post_run_reset(&mut self) { - unsafe { - SetThreadpoolTimer(self.ptp_timer, None, 0, 0); - } - self.executor.post_run_reset(); - } -} - -#[cfg(target_os = "linux")] -impl Executor for TimeoutExecutor -where - E: Executor, - EM: UsesState, - Z: UsesState, -{ - fn run_target( - &mut self, - fuzzer: &mut Z, - state: &mut Self::State, - mgr: &mut EM, - input: &Self::Input, - ) -> Result { - unsafe { - if self.batch_mode { - let data = &mut GLOBAL_STATE; - write_volatile( - &mut data.timeout_executor_ptr, - self as *mut _ as *mut c_void, - ); - - if self.executions == 0 { - libc::timer_settime(self.timerid, 0, addr_of_mut!(self.itimerspec), null_mut()); - self.tmout_start_time = current_time(); - } - self.start_time = current_time(); - } else { - libc::timer_settime(self.timerid, 0, addr_of_mut!(self.itimerspec), null_mut()); - } - - let ret = self.executor.run_target(fuzzer, state, mgr, input); - // reset timer - self.post_run_reset(); - ret - } - } - - fn post_run_reset(&mut self) { - if self.batch_mode { - unsafe { - let elapsed = current_time() - self.tmout_start_time; - // elapsed may be > than tmout in case of received but ingored signal - if elapsed > self.exec_tmout - || self.exec_tmout - elapsed < self.avg_exec_time * self.avg_mul_k - { - let disarmed: libc::itimerspec = zeroed(); - libc::timer_settime(self.timerid, 0, addr_of!(disarmed), null_mut()); - // set timer the next exec - if self.executions > 0 { - self.avg_exec_time = elapsed / self.executions; - self.executions = 0; - } - // readjust K - if self.last_signal_time > self.exec_tmout * self.avg_mul_k - && self.avg_mul_k > 1 - { - self.avg_mul_k -= 1; - } - } else { - self.executions += 1; - } - } - } else { - unsafe { - let disarmed: libc::itimerspec = zeroed(); - libc::timer_settime(self.timerid, 0, addr_of!(disarmed), null_mut()); - } - } - self.executor.post_run_reset(); - } -} - -#[cfg(all(unix, not(target_os = "linux")))] -impl Executor for TimeoutExecutor -where - E: Executor, - EM: UsesState, - Z: UsesState, -{ - fn run_target( - &mut self, - fuzzer: &mut Z, - state: &mut Self::State, - mgr: &mut EM, - input: &Self::Input, - ) -> Result { - unsafe { - setitimer(ITIMER_REAL, &mut self.itimerval, null_mut()); - let ret = self.executor.run_target(fuzzer, state, mgr, input); - self.post_run_reset(); - ret - } - } - - fn post_run_reset(&mut self) { - unsafe { - let mut itimerval_zero: Itimerval = zeroed(); - setitimer(ITIMER_REAL, &mut itimerval_zero, null_mut()); - } - self.executor.post_run_reset(); - } -} - -impl UsesState for TimeoutExecutor -where - E: UsesState, -{ - type State = E::State; -} - -impl UsesObservers for TimeoutExecutor -where - E: UsesObservers, -{ - type Observers = E::Observers; -} - -impl HasObservers for TimeoutExecutor -where - E: HasObservers, -{ - #[inline] - fn observers(&self) -> &Self::Observers { - self.executor.observers() - } - - #[inline] - fn observers_mut(&mut self) -> &mut Self::Observers { - self.executor.observers_mut() - } -} diff --git a/libafl/src/executors/with_observers.rs b/libafl/src/executors/with_observers.rs index 9c2e5c6c04..78594ad776 100644 --- a/libafl/src/executors/with_observers.rs +++ b/libafl/src/executors/with_observers.rs @@ -29,9 +29,7 @@ where mgr: &mut EM, input: &Self::Input, ) -> Result { - let ret = self.executor.run_target(fuzzer, state, mgr, input); - self.executor.post_run_reset(); - ret + self.executor.run_target(fuzzer, state, mgr, input) } } diff --git a/libafl_frida/src/executor.rs b/libafl_frida/src/executor.rs index 2f274eb092..e918977bad 100644 --- a/libafl_frida/src/executor.rs +++ b/libafl_frida/src/executor.rs @@ -7,7 +7,7 @@ use frida_gum::{ }; #[cfg(windows)] use libafl::{ - executors::inprocess::{HasInProcessHandlers, InProcessHandlers}, + executors::{hooks::inprocess::InProcessHooks, inprocess::HasInProcessHooks}, state::{HasCorpus, HasSolutions}, }; use libafl::{ @@ -230,7 +230,7 @@ where } #[cfg(windows)] -impl<'a, 'b, 'c, H, OT, RT, S> HasInProcessHandlers +impl<'a, 'b, 'c, H, OT, RT, S> HasInProcessHooks for FridaInProcessExecutor<'a, 'b, 'c, H, OT, RT, S> where H: FnMut(&S::Input) -> ExitKind, @@ -241,7 +241,13 @@ where { /// the timeout handler #[inline] - fn inprocess_handlers(&self) -> &InProcessHandlers { - &self.base.handlers() + fn inprocess_hooks(&self) -> &InProcessHooks { + &self.base.hooks().0 + } + + /// the timeout handler + #[inline] + fn inprocess_hooks_mut(&mut self) -> &mut InProcessHooks { + &mut self.base.hooks_mut().0 } } diff --git a/libafl_libfuzzer/libafl_libfuzzer_runtime/src/lib.rs b/libafl_libfuzzer/libafl_libfuzzer_runtime/src/lib.rs index f0d6d7fd70..1c790ee4c5 100644 --- a/libafl_libfuzzer/libafl_libfuzzer_runtime/src/lib.rs +++ b/libafl_libfuzzer/libafl_libfuzzer_runtime/src/lib.rs @@ -156,7 +156,7 @@ macro_rules! fuzz_with { }; use libafl::{ corpus::Corpus, - executors::{ExitKind, InProcessExecutor, TimeoutExecutor}, + executors::{ExitKind, InProcessExecutor}, feedback_and_fast, feedback_not, feedback_or, feedback_or_fast, feedbacks::{ConstFeedback, CrashFeedback, MaxMapFeedback, NewHashFeedback, TimeFeedback, TimeoutFeedback}, generators::RandBytesGenerator, @@ -434,16 +434,14 @@ macro_rules! fuzz_with { let mut tracing_harness = harness; // Create the executor for an in-process function with one observer for edge coverage and one for the execution time - let mut executor = TimeoutExecutor::new( - InProcessExecutor::new( + let mut executor = InProcessExecutor::with_timeout( &mut harness, tuple_list!(edges_observer, size_edges_observer, time_observer, backtrace_observer, oom_observer), &mut fuzzer, &mut state, &mut mgr, - )?, - $options.timeout(), - ); + $options.timeout(), + )?; // In case the corpus is empty (on first run) or crashed while loading, reset if state.must_load_initial_inputs() { diff --git a/libafl_libfuzzer/libafl_libfuzzer_runtime/src/merge.rs b/libafl_libfuzzer/libafl_libfuzzer_runtime/src/merge.rs index f8d26811e1..fdba4f9561 100644 --- a/libafl_libfuzzer/libafl_libfuzzer_runtime/src/merge.rs +++ b/libafl_libfuzzer/libafl_libfuzzer_runtime/src/merge.rs @@ -10,7 +10,7 @@ use std::{ use libafl::{ corpus::Corpus, events::{EventRestarter, SimpleRestartingEventManager}, - executors::{ExitKind, InProcessExecutor, TimeoutExecutor}, + executors::{ExitKind, InProcessExecutor}, feedback_and_fast, feedback_or_fast, feedbacks::{CrashFeedback, MinMapFeedback, TimeoutFeedback}, inputs::{BytesInput, HasTargetBytes}, @@ -176,10 +176,14 @@ pub fn merge( }; // Create the executor for an in-process function with one observer for edge coverage and one for the execution time - let mut executor = TimeoutExecutor::new( - InProcessExecutor::new(&mut harness, observers, &mut fuzzer, &mut state, &mut mgr)?, + let mut executor = InProcessExecutor::with_timeout( + &mut harness, + observers, + &mut fuzzer, + &mut state, + &mut mgr, options.timeout(), - ); + )?; // In case the corpus is empty (on first run) or crashed while loading, reset if state.must_load_initial_inputs() && !options.dirs().is_empty() { diff --git a/libafl_libfuzzer/libafl_libfuzzer_runtime/src/tmin.rs b/libafl_libfuzzer/libafl_libfuzzer_runtime/src/tmin.rs index 528e60b603..aae4d56a83 100644 --- a/libafl_libfuzzer/libafl_libfuzzer_runtime/src/tmin.rs +++ b/libafl_libfuzzer/libafl_libfuzzer_runtime/src/tmin.rs @@ -6,7 +6,7 @@ use std::{ use libafl::{ corpus::{Corpus, HasTestcase, InMemoryCorpus, Testcase}, events::SimpleEventManager, - executors::{inprocess::TimeoutInProcessForkExecutor, ExitKind}, + executors::{inprocess_fork::InProcessForkExecutor, ExitKind}, feedbacks::{CrashFeedbackFactory, TimeoutFeedbackFactory}, inputs::{BytesInput, HasBytesVec, HasTargetBytes}, mutators::{havoc_mutations_no_crossover, Mutator, StdScheduledMutator}, @@ -62,7 +62,7 @@ fn minimize_crash_with_mutator>( let mut fuzzer = StdFuzzer::new(QueueScheduler::new(), (), ()); let shmem_provider = StdShMemProvider::new()?; - let mut executor = TimeoutInProcessForkExecutor::new( + let mut executor = InProcessForkExecutor::new( &mut harness, (), &mut fuzzer, diff --git a/libafl_qemu/src/executor.rs b/libafl_qemu/src/executor.rs index 77418354cc..602399c303 100644 --- a/libafl_qemu/src/executor.rs +++ b/libafl_qemu/src/executor.rs @@ -2,6 +2,7 @@ use core::{ ffi::c_void, fmt::{self, Debug, Formatter}, + time::Duration, }; #[cfg(feature = "fork")] @@ -13,7 +14,8 @@ use libafl::{ use libafl::{ events::{EventFirer, EventRestarter}, executors::{ - inprocess::{InProcessExecutor, InProcessExecutorHandlerData}, + hooks::{inprocess::InProcessExecutorHandlerData, ExecutorHooksTuple}, + inprocess::{HasInProcessHooks, InProcessExecutor}, Executor, ExitKind, HasObservers, }, feedbacks::Feedback, @@ -97,7 +99,7 @@ pub unsafe fn inproc_qemu_timeout_handler( context: Option<&mut ucontext_t>, data: &mut InProcessExecutorHandlerData, ) where - E: Executor + HasObservers, + E: Executor + HasObservers + HasInProcessHooks, EM: EventFirer + EventRestarter, OF: Feedback, E::State: HasSolutions + HasCorpus + HasExecutions, @@ -106,7 +108,7 @@ pub unsafe fn inproc_qemu_timeout_handler( if BREAK_ON_TMOUT { qemu_system_debug_request(); } else { - libafl::executors::inprocess::unix_signal_handler::inproc_timeout_handler::( + libafl::executors::hooks::unix::unix_signal_handler::inproc_timeout_handler::( signal, info, context, data, ); } @@ -126,6 +128,7 @@ where fuzzer: &mut Z, state: &mut S, event_mgr: &mut EM, + timeout: Duration, ) -> Result where EM: EventFirer + EventRestarter, @@ -133,10 +136,12 @@ where S: State + HasExecutions + HasCorpus + HasSolutions, Z: HasObjective, { - let mut inner = InProcessExecutor::new(harness_fn, observers, fuzzer, state, event_mgr)?; + let mut inner = InProcessExecutor::with_timeout( + harness_fn, observers, fuzzer, state, event_mgr, timeout, + )?; #[cfg(emulation_mode = "usermode")] { - inner.handlers_mut().crash_handler = + inner.inprocess_hooks_mut().crash_handler = inproc_qemu_crash_handler::, EM, OF, Z> as *const c_void; @@ -157,7 +162,7 @@ where } #[cfg(emulation_mode = "systemmode")] { - inner.handlers_mut().timeout_handler = + inner.inprocess_hooks_mut().timeout_handler = inproc_qemu_timeout_handler::, EM, OF, Z> as *const c_void; } @@ -315,6 +320,7 @@ where state: &mut S, event_mgr: &mut EM, shmem_provider: SP, + timeout: core::time::Duration, ) -> Result where EM: EventFirer + EventRestarter, @@ -333,6 +339,7 @@ where fuzzer, state, event_mgr, + timeout, shmem_provider, )?, }) diff --git a/libafl_qemu/src/hooks.rs b/libafl_qemu/src/hooks.rs index feee6ae9ab..d0284a5d89 100644 --- a/libafl_qemu/src/hooks.rs +++ b/libafl_qemu/src/hooks.rs @@ -9,10 +9,7 @@ use core::{ ptr::{self, addr_of}, }; -use libafl::{ - executors::{inprocess::inprocess_get_state, ExitKind}, - inputs::UsesInput, -}; +use libafl::{executors::hooks::inprocess::inprocess_get_state, inputs::UsesInput}; pub use crate::emu::SyscallHookResult; use crate::{ diff --git a/libafl_sugar/src/inmemory.rs b/libafl_sugar/src/inmemory.rs index b73e1f8792..c799534e75 100644 --- a/libafl_sugar/src/inmemory.rs +++ b/libafl_sugar/src/inmemory.rs @@ -7,7 +7,7 @@ use std::{fs, net::SocketAddr, path::PathBuf, time::Duration}; use libafl::{ corpus::{CachedOnDiskCorpus, Corpus, OnDiskCorpus}, events::{launcher::Launcher, EventConfig, EventRestarter, LlmpRestartingEventManager}, - executors::{inprocess::InProcessExecutor, ExitKind, ShadowExecutor, TimeoutExecutor}, + executors::{inprocess::InProcessExecutor, ExitKind, ShadowExecutor}, feedback_or, feedback_or_fast, feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, StdFuzzer}, @@ -201,16 +201,14 @@ where // Create the executor for an in-process function with one observer for edge coverage and one for the execution time let mut executor = ShadowExecutor::new( - TimeoutExecutor::new( - InProcessExecutor::new( - &mut harness, - tuple_list!(edges_observer, time_observer), - &mut fuzzer, - &mut state, - &mut mgr, - )?, + InProcessExecutor::with_timeout( + &mut harness, + tuple_list!(edges_observer, time_observer), + &mut fuzzer, + &mut state, + &mut mgr, timeout, - ), + )?, tuple_list!(cmplog_observer), ); diff --git a/libafl_sugar/src/qemu.rs b/libafl_sugar/src/qemu.rs index 3a32e9a05b..092a88fb3a 100644 --- a/libafl_sugar/src/qemu.rs +++ b/libafl_sugar/src/qemu.rs @@ -9,7 +9,7 @@ use std::{fs, net::SocketAddr, path::PathBuf, time::Duration}; use libafl::{ corpus::{CachedOnDiskCorpus, Corpus, OnDiskCorpus}, events::{launcher::Launcher, EventConfig, EventRestarter, LlmpRestartingEventManager}, - executors::{ExitKind, ShadowExecutor, TimeoutExecutor}, + executors::{ExitKind, ShadowExecutor}, feedback_or, feedback_or_fast, feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, StdFuzzer}, @@ -231,8 +231,8 @@ where &mut fuzzer, &mut state, &mut mgr, + timeout, )?; - let executor = TimeoutExecutor::new(executor, timeout); let mut executor = ShadowExecutor::new(executor, tuple_list!(cmplog_observer)); // In case the corpus is empty (on first run), reset @@ -330,15 +330,15 @@ where tuple_list!(QemuEdgeCoverageHelper::default()), ); - let executor = QemuExecutor::new( + let mut executor = QemuExecutor::new( &mut hooks, &mut harness, tuple_list!(edges_observer, time_observer), &mut fuzzer, &mut state, &mut mgr, + timeout, )?; - let mut executor = TimeoutExecutor::new(executor, timeout); // In case the corpus is empty (on first run), reset if state.must_load_initial_inputs() { diff --git a/libafl_targets/src/windows_asan.rs b/libafl_targets/src/windows_asan.rs index e462d23bd6..86bd9d7169 100644 --- a/libafl_targets/src/windows_asan.rs +++ b/libafl_targets/src/windows_asan.rs @@ -2,7 +2,7 @@ use libafl::{ events::{EventFirer, EventRestarter}, - executors::{inprocess::windows_asan_handler::asan_death_handler, Executor, HasObservers}, + executors::{hooks::windows::windows_asan_handler::asan_death_handler, Executor, HasObservers}, feedbacks::Feedback, state::{HasCorpus, HasExecutions, HasSolutions}, HasObjective,