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:
parent
058d2c0825
commit
2ac154d473
2
.gitignore
vendored
2
.gitignore
vendored
@ -66,3 +66,5 @@ libafl_nyx/packer
|
||||
.gdb_history
|
||||
# No llvm IR
|
||||
*.ll
|
||||
|
||||
.tar.gz
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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");
|
||||
|
@ -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");
|
||||
|
@ -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");
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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| {
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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();
|
||||
|
@ -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.
|
||||
|
@ -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)]
|
||||
|
@ -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()));
|
||||
|
@ -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());
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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)]
|
||||
|
@ -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));
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
|
471
libafl/src/executors/hooks/inprocess.rs
Normal file
471
libafl/src/executors/hooks/inprocess.rs
Normal 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 }
|
||||
}
|
172
libafl/src/executors/hooks/inprocess_fork.rs
Normal file
172
libafl/src/executors/hooks/inprocess_fork.rs
Normal 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()
|
||||
}
|
||||
}
|
102
libafl/src/executors/hooks/mod.rs
Normal file
102
libafl/src/executors/hooks/mod.rs
Normal 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);
|
||||
}
|
||||
}
|
378
libafl/src/executors/hooks/timer.rs
Normal file
378
libafl/src/executors/hooks/timer.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
272
libafl/src/executors/hooks/unix.rs
Normal file
272
libafl/src/executors/hooks/unix.rs
Normal 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));
|
||||
}
|
||||
}
|
418
libafl/src/executors/hooks/windows.rs
Normal file
418
libafl/src/executors/hooks/windows.rs
Normal 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
613
libafl/src/executors/inprocess_fork.rs
Normal file
613
libafl/src/executors/inprocess_fork.rs
Normal 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();
|
||||
}
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
//! Executors take input, and run it in the target.
|
||||
|
||||
#[cfg(unix)]
|
||||
use alloc::vec::Vec;
|
||||
use core::fmt::Debug;
|
||||
|
||||
pub use combined::CombinedExecutor;
|
||||
@ -10,11 +12,11 @@ pub use differential::DiffExecutor;
|
||||
pub use forkserver::{Forkserver, ForkserverExecutor, TimeoutForkserverExecutor};
|
||||
pub use inprocess::InProcessExecutor;
|
||||
#[cfg(all(feature = "std", feature = "fork", unix))]
|
||||
pub use inprocess::InProcessForkExecutor;
|
||||
pub use inprocess_fork::InProcessForkExecutor;
|
||||
#[cfg(unix)]
|
||||
use libafl_bolts::os::unix_signals::Signal;
|
||||
use serde::{Deserialize, Serialize};
|
||||
pub use shadow::ShadowExecutor;
|
||||
#[cfg(any(unix, feature = "std"))]
|
||||
pub use timeout::TimeoutExecutor;
|
||||
pub use with_observers::WithObservers;
|
||||
|
||||
use crate::{
|
||||
@ -30,13 +32,18 @@ pub mod differential;
|
||||
#[cfg(all(feature = "std", feature = "fork", unix))]
|
||||
pub mod forkserver;
|
||||
pub mod inprocess;
|
||||
|
||||
/// The module for inproc fork executor
|
||||
#[cfg(all(feature = "std", unix))]
|
||||
pub mod inprocess_fork;
|
||||
|
||||
pub mod shadow;
|
||||
/// Timeout executor.
|
||||
/// Not possible on `no-std` Windows or `no-std`, but works for unix
|
||||
#[cfg(any(unix, feature = "std"))]
|
||||
pub mod timeout;
|
||||
|
||||
pub mod with_observers;
|
||||
|
||||
/// The module for all the hooks
|
||||
pub mod hooks;
|
||||
|
||||
/// How an execution finished.
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[cfg_attr(
|
||||
@ -135,10 +142,25 @@ where
|
||||
{
|
||||
WithObservers::new(self, observers)
|
||||
}
|
||||
}
|
||||
|
||||
/// Custom Reset Handler, e.g., to reset timers
|
||||
#[inline]
|
||||
fn post_run_reset(&mut self) {}
|
||||
/// The common signals we want to handle
|
||||
#[cfg(unix)]
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn common_signals() -> Vec<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)]
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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() {
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
)?,
|
||||
})
|
||||
|
@ -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::{
|
||||
|
@ -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),
|
||||
);
|
||||
|
||||
|
@ -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() {
|
||||
|
@ -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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user