Refactor InProcessExecutor, merge timeout executors (#1789)

* move windows, inprocess fork to a different file, try new hook mechanism for the executor

* fix

* even more

* more

* more

* fix

* fix

* macosgit add -ugit add -u

* windows!

* windows!

* aa

* aa

* macos

* std

* wtf unresolved?

* Copy, Clone

* why you just don't have the same API!

* inproc

* next; inprocess

* windows?

* ci

* ci

* ci

* unused

* ci

* unused

* no_std

* windows no std

* fix

* inprocess

* fix

* windows

* fuzzers

* macos , book

* fix

* aa

* allow

* fix

* stop suggesting wrong lint AAAAAAAAAAAAAAAAA!!!

* stop suggesting wrong lint AAAAAAAAAAAAAAAAA!!!

* win

* fix

* wip

* wip2

* windows done?

* remove TimeoutExecutor

* ci

* ci

* miri

* fixfi

* compile on windows

* a

* clp

* no_std stuff

* windows no_std

* mac stuff

* m

* a

* ci

* ci

* deleting timeoutexecutor, gradually

* fucking macos

* ci

* test

* ci

* ci

* batch mode constructor

* fix

* ci

* aa

* miri

* aaa

* tmate again

* fix windows stuff

* final fix

* another win fix

* add

* let's add the new fix later

* more

* fi

* parse

* win clippy

* win no std

* safety

* fix

* DEFAULT

* final fix

* libafl_libfuzzer

* comments

* fix

* fix fuzzres

* fixxxxx

* fixxxxx

* last fix

* change name
This commit is contained in:
Dongjia "toka" Zhang 2024-01-23 22:35:14 +01:00 committed by GitHub
parent 058d2c0825
commit 2ac154d473
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
52 changed files with 3037 additions and 2806 deletions

2
.gitignore vendored
View File

@ -66,3 +66,5 @@ libafl_nyx/packer
.gdb_history
# No llvm IR
*.ll
.tar.gz

View File

@ -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,7 +71,13 @@ 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)
let mut executor = InProcessExecutor::new(
&mut harness,
(),
&mut fuzzer,
&mut state,
&mut mgr,
)
.expect("Failed to create the Executor");
/* ANCHOR_END: executor */

View File

@ -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.

View File

@ -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");

View File

@ -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");

View File

@ -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");

View File

@ -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(
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);

View File

@ -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

View File

@ -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));

View File

@ -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(
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,
)?,
// Give it more time!
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(
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,
)?,
// Give it more time!
timeout * 10,
));
)?);
// The order of the stages matter!
let mut stages = tuple_list!(generalization, calibration, tracing, i2s, power, grimoire);

View File

@ -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(
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| {

View File

@ -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(
let mut executor = InProcessExecutor::with_timeout(
&mut harness,
tuple_list!(edges_observer, time_observer),
&mut fuzzer,
&mut state,
&mut restarting_mgr,
)?,
// 10 seconds timeout
Duration::new(10, 0),
);
)?;
// 10 seconds timeout
// The actual target run starts here.
// Call LLVMFUzzerInitialize() if present.

View File

@ -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(
let mut executor = InProcessExecutor::with_timeout(
&mut harness,
tuple_list!(edges_observer, time_observer),
&mut fuzzer,
&mut state,
&mut restarting_mgr,
)?,
// 10 seconds timeout
opt.timeout,
);
)?;
// The actual target run starts here.
// Call LLVMFUzzerInitialize() if present.

View File

@ -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(
let mut executor = InProcessExecutor::with_timeout(
&mut harness,
tuple_list!(edges_observer, time_observer),
&mut fuzzer,
&mut state,
&mut restarting_mgr,
)?,
// 10 seconds timeout
Duration::new(10, 0),
);
)?;
// The actual target run starts here.
// Call LLVMFUzzerInitialize() if present.

View File

@ -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.

View File

@ -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(
let mut executor = InProcessExecutor::with_timeout(
&mut harness,
tuple_list!(edges_observer, time_observer),
&mut fuzzer,
&mut state,
&mut restarting_mgr,
)?,
// 10 seconds timeout
Duration::new(10, 0),
);
)?;
// The actual target run starts here.
// Call LLVMFUzzerInitialize() if present.

View File

@ -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(
let mut executor = InProcessExecutor::with_timeout(
&mut harness,
tuple_list!(edges_observer, time_observer),
&mut fuzzer,
&mut state,
&mut restarting_mgr,
)?,
// 10 seconds timeout
opt.timeout,
);
)?;
// The actual target run starts here.
// Call LLVMFUzzerInitialize() if present.

View File

@ -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.

View File

@ -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(
let mut executor = InProcessExecutor::with_timeout(
&mut harness,
tuple_list!(edges_observer, time_observer),
&mut fuzzer,
&mut state,
&mut restarting_mgr,
)?,
// 10 seconds timeout
opt.timeout,
);
)?;
// The actual target run starts here.
// Call LLVMFUzzerInitialize() if present.
let args: Vec<String> = env::args().collect();

View File

@ -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(
let mut executor = InProcessExecutor::with_timeout(
&mut harness,
tuple_list!(edges_observer, time_observer),
&mut fuzzer,
&mut state,
&mut restarting_mgr,
)?,
// 10 seconds timeout
Duration::new(10, 0),
);
)?;
// The actual target run starts here.
// Call LLVMFUzzerInitialize() if present.

View File

@ -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(
let mut executor = InProcessExecutor::with_timeout(
&mut harness,
tuple_list!(edges_observer, time_observer),
&mut fuzzer,
&mut state,
&mut restarting_mgr,
)?,
// 10 seconds timeout
Duration::new(10, 0),
);
)?;
// Initialize ASAN, call this before any ASAN crashes can occur (directly after initializing executor e.g.)
#[cfg(windows)]

View File

@ -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()));

View File

@ -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());

View File

@ -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<Duration, Error> {
Ok(Duration::from_millis(time.parse()?))
}
impl From<Version> 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)

View File

@ -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);

View File

@ -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)]

View File

@ -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));

View File

@ -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)

View File

@ -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(
let mut executor = InProcessExecutor::with_timeout(
&mut harness,
tuple_list!(edges_observer, time_observer),
&mut fuzzer,
&mut state,
&mut restarting_mgr,
)?,
// 10 seconds timeout
Duration::new(10, 0),
);
)?;
// The actual target run starts here.
// Call LLVMFUzzerInitialize() if present.

View File

@ -57,10 +57,7 @@ where
) -> Result<ExitKind, Error> {
*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)
}
}

View File

@ -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()

View File

@ -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<E: HasObservers, S>(&mut self, _state: &mut S) {}
/// Call before running a target.
#[allow(clippy::unused_self)]
#[allow(unused_variables)]
fn pre_exec<EM, I, S, Z>(&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<EM, I, S, Z>(
&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<E, EM, OF, Z>(exec_tmout: Duration) -> Result<Self, Error>
where
E: Executor<EM, Z> + HasObservers + HasInProcessHooks,
EM: EventFirer<State = E::State> + EventRestarter<State = E::State>,
OF: Feedback<E::State>,
E::State: HasExecutions + HasSolutions + HasCorpus,
Z: HasObjective<Objective = OF, State = E::State>,
{
#[cfg_attr(miri, allow(unused_variables))]
unsafe {
let data = &mut GLOBAL_STATE;
#[cfg(feature = "std")]
unix_signal_handler::setup_panic_hook::<E, EM, OF, Z>();
#[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::<E, EM, OF, Z>
as *const c_void,
#[cfg(feature = "std")]
timeout_handler: unix_signal_handler::inproc_timeout_handler::<E, EM, OF, Z>
as *const _,
#[cfg(feature = "std")]
timer: TimerStruct::new(exec_tmout),
})
}
}
/// Create new [`InProcessHooks`].
#[cfg(windows)]
#[allow(unused)]
pub fn new<E, EM, OF, Z>(exec_tmout: Duration) -> Result<Self, Error>
where
E: Executor<EM, Z> + HasObservers + HasInProcessHooks,
EM: EventFirer<State = E::State> + EventRestarter<State = E::State>,
OF: Feedback<E::State>,
E::State: State + HasExecutions + HasSolutions + HasCorpus,
Z: HasObjective<Objective = OF, State = E::State>,
{
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<E, EM, OF, Z>(exec_tmout: Duration) -> Result<Self, Error>
where
E: Executor<EM, Z> + HasObservers + HasInProcessHooks,
EM: EventFirer<State = E::State> + EventRestarter<State = E::State>,
OF: Feedback<E::State>,
E::State: HasExecutions + HasSolutions + HasCorpus,
Z: HasObjective<Objective = OF, State = E::State>,
{
#[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<PTP_TIMER>,
#[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 }
}

View File

@ -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<E: HasObservers, S>(&mut self, _state: &mut S) {}
/// Call before running a target.
fn pre_exec<EM, I, S, Z>(
&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<EM, I, S, Z>(
&mut self,
_fuzzer: &mut Z,
_state: &mut S,
_mgr: &mut EM,
_input: &I,
) {
}
}
impl InChildProcessHooks {
/// Create new [`InChildProcessHooks`].
pub fn new<E>() -> Result<Self, Error>
where
E: HasObservers,
{
#[cfg_attr(miri, allow(unused_variables))]
unsafe {
let data = &mut FORK_EXECUTOR_GLOBAL_DATA;
// child_signal_handlers::setup_child_panic_hook::<E, I, OT, S>();
#[cfg(not(miri))]
setup_signal_handler(data)?;
compiler_fence(Ordering::SeqCst);
Ok(Self {
crash_handler: child_signal_handlers::child_crash_handler::<E> as *const c_void,
timeout_handler: child_signal_handlers::child_timeout_handler::<E> 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<Signal> {
common_signals()
}
}

View File

@ -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<E: HasObservers, S>(&mut self, state: &mut S);
/// The hook that runs before runs the target
fn pre_exec<EM, I, S, Z>(&mut self, fuzzer: &mut Z, state: &mut S, mgr: &mut EM, input: &I);
/// The hook that runs before runs the target
fn post_exec<EM, I, S, Z>(&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<E: HasObservers, S>(&mut self, state: &mut S);
/// The hooks that runs before runs the target
fn pre_exec_all<EM, I, S, Z>(&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<EM, I, S, Z>(
&mut self,
fuzzer: &mut Z,
state: &mut S,
mgr: &mut EM,
input: &I,
);
}
impl ExecutorHooksTuple for () {
fn init_all<E, S>(&mut self, _state: &mut S) {}
fn pre_exec_all<EM, I, S, Z>(
&mut self,
_fuzzer: &mut Z,
_state: &mut S,
_mgr: &mut EM,
_input: &I,
) {
}
fn post_exec_all<EM, I, S, Z>(
&mut self,
_fuzzer: &mut Z,
_state: &mut S,
_mgr: &mut EM,
_input: &I,
) {
}
}
impl<Head, Tail> ExecutorHooksTuple for (Head, Tail)
where
Head: ExecutorHook,
Tail: ExecutorHooksTuple,
{
fn init_all<E: HasObservers, S>(&mut self, state: &mut S) {
self.0.init::<E, S>(state);
self.1.init_all::<E, S>(state);
}
fn pre_exec_all<EM, I, S, Z>(
&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<EM, I, S, Z>(
&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);
}
}

View File

@ -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);
}
}
}

View File

@ -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<Signal> {
common_signals()
}
}
/// invokes the `post_exec` hook on all observer in case of panic
pub fn setup_panic_hook<E, EM, OF, Z>()
where
E: HasObservers,
EM: EventFirer<State = E::State> + EventRestarter<State = E::State>,
OF: Feedback<E::State>,
E::State: HasExecutions + HasSolutions + HasCorpus,
Z: HasObjective<Objective = OF, State = E::State>,
{
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::<E>();
let state = data.state_mut::<E::State>();
let input = data.take_current_input::<<E::State as UsesInput>::Input>();
let fuzzer = data.fuzzer_mut::<Z>();
let event_mgr = data.event_mgr_mut::<EM>();
run_observers_and_save_state::<E, EM, OF, Z>(
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<E, EM, OF, Z>(
_signal: Signal,
_info: &mut siginfo_t,
_context: Option<&mut ucontext_t>,
data: &mut InProcessExecutorHandlerData,
) where
E: HasObservers + HasInProcessHooks,
EM: EventFirer<State = E::State> + EventRestarter<State = E::State>,
OF: Feedback<E::State>,
E::State: HasExecutions + HasSolutions + HasCorpus,
Z: HasObjective<Objective = OF, State = E::State>,
{
// this stuff is for batch timeout
if !data.executor_ptr.is_null()
&& data
.executor_mut::<E>()
.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::<E>();
let state = data.state_mut::<E::State>();
let event_mgr = data.event_mgr_mut::<EM>();
let fuzzer = data.fuzzer_mut::<Z>();
let input = data.take_current_input::<<E::State as UsesInput>::Input>();
log::error!("Timeout in fuzz run.");
run_observers_and_save_state::<E, EM, OF, Z>(
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<E, EM, OF, Z>(
signal: Signal,
_info: &mut siginfo_t,
_context: Option<&mut ucontext_t>,
data: &mut InProcessExecutorHandlerData,
) where
E: Executor<EM, Z> + HasObservers,
EM: EventFirer<State = E::State> + EventRestarter<State = E::State>,
OF: Feedback<E::State>,
E::State: HasExecutions + HasSolutions + HasCorpus,
Z: HasObjective<Objective = OF, State = E::State>,
{
#[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::<E>();
// disarms timeout in case of timeout
let state = data.state_mut::<E::State>();
let event_mgr = data.event_mgr_mut::<EM>();
let fuzzer = data.fuzzer_mut::<Z>();
let input = data.take_current_input::<<E::State as UsesInput>::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::<E, EM, OF, Z>(
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));
}
}

View File

@ -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<E, EM, OF, Z>()
where
E: Executor<EM, Z> + HasObservers,
EM: EventFirer<State = E::State> + EventRestarter<State = E::State>,
OF: Feedback<E::State>,
E::State: HasExecutions + HasSolutions + HasCorpus,
Z: HasObjective<Objective = OF, State = E::State>,
{
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::<E>();
// reset timer
if data.ptp_timer.is_some() {
data.ptp_timer = None;
}
let state = data.state_mut::<E::State>();
let fuzzer = data.fuzzer_mut::<Z>();
let event_mgr = data.event_mgr_mut::<EM>();
log::error!("Child crashed!");
// Make sure we don't crash in the crash handler forever.
let input = data.take_current_input::<<E::State as UsesInput>::Input>();
run_observers_and_save_state::<E, EM, OF, Z>(
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<ExceptionCode> {
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<E, EM, OF, Z>()
where
E: HasObservers,
EM: EventFirer<State = E::State> + EventRestarter<State = E::State>,
OF: Feedback<E::State>,
E::State: HasExecutions + HasSolutions + HasCorpus,
Z: HasObjective<Objective = OF, State = E::State>,
{
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::<E>();
let state = data.state_mut::<E::State>();
let fuzzer = data.fuzzer_mut::<Z>();
let event_mgr = data.event_mgr_mut::<EM>();
let input = data.take_current_input::<<E::State as UsesInput>::Input>();
run_observers_and_save_state::<E, EM, OF, Z>(
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<E, EM, OF, Z>(
_p0: *mut u8,
global_state: *mut c_void,
_p1: *mut u8,
) where
E: HasObservers + HasInProcessHooks,
EM: EventFirer<State = E::State> + EventRestarter<State = E::State>,
OF: Feedback<E::State>,
E::State: State + HasExecutions + HasSolutions + HasCorpus,
Z: HasObjective<Objective = OF, State = E::State>,
{
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::<E>()
.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::<E>();
let state = data.state_mut::<E::State>();
let fuzzer = data.fuzzer_mut::<Z>();
let event_mgr = data.event_mgr_mut::<EM>();
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 <E::State as UsesInput>::Input)
.as_ref()
.unwrap();
data.current_input_ptr = ptr::null_mut();
run_observers_and_save_state::<E, EM, OF, Z>(
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<E, EM, OF, Z>(
exception_pointers: *mut EXCEPTION_POINTERS,
data: &mut InProcessExecutorHandlerData,
) where
E: Executor<EM, Z> + HasObservers,
EM: EventFirer<State = E::State> + EventRestarter<State = E::State>,
OF: Feedback<E::State>,
E::State: HasExecutions + HasSolutions + HasCorpus,
Z: HasObjective<Objective = OF, State = E::State>,
{
// 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::<E>();
// reset timer
if data.ptp_timer.is_some() {
data.ptp_timer = None;
}
let state = data.state_mut::<E::State>();
let fuzzer = data.fuzzer_mut::<Z>();
let event_mgr = data.event_mgr_mut::<EM>();
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::<<E::State as UsesInput>::Input>();
run_observers_and_save_state::<E, EM, OF, Z>(
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!");
}
}

File diff suppressed because it is too large Load Diff

View File

@ -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<S>,
SP: ShMemProvider,
{
#[allow(clippy::too_many_arguments)]
/// The constructor for `InProcessForkExecutor`
pub fn new<EM, OF, Z>(
harness_fn: &'a mut H,
observers: OT,
fuzzer: &mut Z,
state: &mut S,
event_mgr: &mut EM,
timeout: Duration,
shmem_provider: SP,
) -> Result<Self, Error>
where
EM: EventFirer<State = S> + EventRestarter<State = S>,
OF: Feedback<S>,
S: HasSolutions,
Z: HasObjective<Objective = OF, State = S>,
{
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>,
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<S>,
}
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<S> + 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>,
S: State,
SP: ShMemProvider,
HT: ExecutorHooksTuple,
{
type State = S;
}
impl<'a, EM, H, HT, OT, S, SP, Z> Executor<EM, Z>
for GenericInProcessForkExecutor<'a, H, HT, OT, S, SP>
where
EM: UsesState<State = S>,
H: FnMut(&S::Input) -> ExitKind + ?Sized,
OT: ObserversTuple<S>,
S: State + HasExecutions,
SP: ShMemProvider,
HT: ExecutorHooksTuple,
Z: UsesState<State = S>,
{
#[allow(unreachable_code)]
#[inline]
fn run_target(
&mut self,
fuzzer: &mut Z,
state: &mut Self::State,
mgr: &mut EM,
input: &Self::Input,
) -> Result<ExitKind, Error> {
*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<S>,
SP: ShMemProvider,
{
#[inline]
/// This function marks the boundary between the fuzzer and the target.
pub fn enter_target<EM, Z>(
&mut self,
_fuzzer: &mut Z,
state: &mut <Self as UsesState>::State,
_event_mgr: &mut EM,
input: &<Self as UsesInput>::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<EM, Z>(
&mut self,
_fuzzer: &mut Z,
_state: &mut <Self as UsesState>::State,
_event_mgr: &mut EM,
_input: &<Self as UsesInput>::Input,
) {
// do nothing
}
/// Creates a new [`GenericInProcessForkExecutor`] with custom hooks
#[cfg(target_os = "linux")]
#[allow(clippy::too_many_arguments)]
pub fn with_hooks<EM, OF, Z>(
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<Self, Error>
where
EM: EventFirer<State = S> + EventRestarter<State = S>,
OF: Feedback<S>,
S: HasSolutions,
Z: HasObjective<Objective = OF, State = S>,
{
let default_hooks = InChildProcessHooks::new::<Self>()?;
let mut hooks = tuple_list!(default_hooks).merge(userhooks);
hooks.init_all::<Self, S>(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<EM, OF, Z>(
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<Self, Error>
where
EM: EventFirer<State = S> + EventRestarter<State = S>,
OF: Feedback<S>,
S: HasSolutions,
Z: HasObjective<Objective = OF, State = S>,
{
let default_hooks = InChildProcessHooks::new::<Self>()?;
let mut hooks = tuple_list!(default_hooks).merge(userhooks);
hooks.init_all::<Self, S>(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>,
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<S>,
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<E>()
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::<E>();
let observers = executor.observers_mut();
let state = data.state_mut::<E::State>();
// Invalidate data to not execute again the observer hooks in the crash handler
let input = data.take_current_input::<<E::State as UsesInput>::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<E>(
_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::<E>();
let observers = executor.observers_mut();
let state = data.state_mut::<E::State>();
let input = data.take_current_input::<<E::State as UsesInput>::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<E>(
_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::<E>();
let observers = executor.observers_mut();
let state = data.state_mut::<E::State>();
let input = data.take_current_input::<<E::State as UsesInput>::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();
}
}

View File

@ -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
/// The common signals we want to handle
#[cfg(unix)]
#[inline]
fn post_run_reset(&mut self) {}
#[must_use]
pub fn common_signals() -> Vec<Signal> {
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)]

View File

@ -70,9 +70,7 @@ where
mgr: &mut EM,
input: &Self::Input,
) -> Result<ExitKind, Error> {
let ret = self.executor.run_target(fuzzer, state, mgr, input);
self.executor.post_run_reset();
ret
self.executor.run_target(fuzzer, state, mgr, input)
}
}

View File

@ -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<E> {
/// 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<E: Debug> Debug for TimeoutExecutor<E> {
#[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<E> TimeoutExecutor<E> {
/// 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<E> TimeoutExecutor<E> {
/// 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<E: HasInProcessHandlers> TimeoutExecutor<E> {
/// 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<E, EM, Z> Executor<EM, Z> for TimeoutExecutor<E>
where
E: Executor<EM, Z> + HasInProcessHandlers,
EM: UsesState<State = E::State>,
Z: UsesState<State = E::State>,
{
#[allow(clippy::cast_sign_loss)]
fn run_target(
&mut self,
fuzzer: &mut Z,
state: &mut Self::State,
mgr: &mut EM,
input: &Self::Input,
) -> Result<ExitKind, Error> {
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<E, EM, Z> Executor<EM, Z> for TimeoutExecutor<E>
where
E: Executor<EM, Z>,
EM: UsesState<State = E::State>,
Z: UsesState<State = E::State>,
{
fn run_target(
&mut self,
fuzzer: &mut Z,
state: &mut Self::State,
mgr: &mut EM,
input: &Self::Input,
) -> Result<ExitKind, Error> {
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<E, EM, Z> Executor<EM, Z> for TimeoutExecutor<E>
where
E: Executor<EM, Z>,
EM: UsesState<State = E::State>,
Z: UsesState<State = E::State>,
{
fn run_target(
&mut self,
fuzzer: &mut Z,
state: &mut Self::State,
mgr: &mut EM,
input: &Self::Input,
) -> Result<ExitKind, Error> {
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<E> UsesState for TimeoutExecutor<E>
where
E: UsesState,
{
type State = E::State;
}
impl<E> UsesObservers for TimeoutExecutor<E>
where
E: UsesObservers,
{
type Observers = E::Observers;
}
impl<E> HasObservers for TimeoutExecutor<E>
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()
}
}

View File

@ -29,9 +29,7 @@ where
mgr: &mut EM,
input: &Self::Input,
) -> Result<ExitKind, Error> {
let ret = self.executor.run_target(fuzzer, state, mgr, input);
self.executor.post_run_reset();
ret
self.executor.run_target(fuzzer, state, mgr, input)
}
}

View File

@ -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
}
}

View File

@ -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(),
);
)?;
// In case the corpus is empty (on first run) or crashed while loading, reset
if state.must_load_initial_inputs() {

View File

@ -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() {

View File

@ -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<M: Mutator<BytesInput, TMinState>>(
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,

View File

@ -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<E, EM, OF, Z>(
context: Option<&mut ucontext_t>,
data: &mut InProcessExecutorHandlerData,
) where
E: Executor<EM, Z> + HasObservers,
E: Executor<EM, Z> + HasObservers + HasInProcessHooks,
EM: EventFirer<State = E::State> + EventRestarter<State = E::State>,
OF: Feedback<E::State>,
E::State: HasSolutions + HasCorpus + HasExecutions,
@ -106,7 +108,7 @@ pub unsafe fn inproc_qemu_timeout_handler<E, EM, OF, Z>(
if BREAK_ON_TMOUT {
qemu_system_debug_request();
} else {
libafl::executors::inprocess::unix_signal_handler::inproc_timeout_handler::<E, EM, OF, Z>(
libafl::executors::hooks::unix::unix_signal_handler::inproc_timeout_handler::<E, EM, OF, Z>(
signal, info, context, data,
);
}
@ -126,6 +128,7 @@ where
fuzzer: &mut Z,
state: &mut S,
event_mgr: &mut EM,
timeout: Duration,
) -> Result<Self, Error>
where
EM: EventFirer<State = S> + EventRestarter<State = S>,
@ -133,10 +136,12 @@ where
S: State + HasExecutions + HasCorpus + HasSolutions,
Z: HasObjective<Objective = OF, State = S>,
{
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::<InProcessExecutor<'a, H, OT, S>, 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::<InProcessExecutor<'a, H, OT, S>, 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<Self, Error>
where
EM: EventFirer<State = S> + EventRestarter,
@ -333,6 +339,7 @@ where
fuzzer,
state,
event_mgr,
timeout,
shmem_provider,
)?,
})

View File

@ -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::{

View File

@ -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(
InProcessExecutor::with_timeout(
&mut harness,
tuple_list!(edges_observer, time_observer),
&mut fuzzer,
&mut state,
&mut mgr,
)?,
timeout,
),
)?,
tuple_list!(cmplog_observer),
);

View File

@ -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() {

View File

@ -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,