Add DrCov &.rerun options to QEMU_Launcher (#2607)
* Move all target-specific code to harness.rs * Add IfModule to LibAFL_Qemu * More more * Added DrCov tracing * Add DrCov rerun to launcher * fixes
This commit is contained in:
parent
2bfed2d488
commit
66a8682c9e
@ -15,6 +15,7 @@ use libafl_qemu::{
|
||||
asan::{init_qemu_with_asan, AsanModule},
|
||||
asan_guest::{init_qemu_with_asan_guest, AsanGuestModule},
|
||||
cmplog::CmpLogModule,
|
||||
DrCovModule,
|
||||
},
|
||||
Qemu,
|
||||
};
|
||||
@ -38,7 +39,7 @@ impl<'a> Client<'a> {
|
||||
Client { options }
|
||||
}
|
||||
|
||||
fn args(&self) -> Result<Vec<String>, Error> {
|
||||
pub fn args(&self) -> Result<Vec<String>, Error> {
|
||||
let program = env::args()
|
||||
.next()
|
||||
.ok_or_else(|| Error::empty_optional("Failed to read program name"))?;
|
||||
@ -49,7 +50,7 @@ impl<'a> Client<'a> {
|
||||
}
|
||||
|
||||
#[allow(clippy::unused_self)] // Api should look the same as args above
|
||||
fn env(&self) -> Vec<(String, String)> {
|
||||
pub fn env(&self) -> Vec<(String, String)> {
|
||||
env::vars()
|
||||
.filter(|(k, _v)| k != "LD_LIBRARY_PATH")
|
||||
.collect::<Vec<(String, String)>>()
|
||||
@ -125,7 +126,16 @@ impl<'a> Client<'a> {
|
||||
.core_id(core_id)
|
||||
.extra_tokens(extra_tokens);
|
||||
|
||||
if is_asan && is_cmplog {
|
||||
if self.options.rerun_input.is_some() && self.options.drcov.is_some() {
|
||||
// Special code path for re-running inputs with DrCov.
|
||||
// TODO: Add ASan support, injection support
|
||||
let drcov = self.options.drcov.as_ref().unwrap();
|
||||
let drcov = DrCovModule::builder()
|
||||
.filename(drcov.clone())
|
||||
.full_trace(true)
|
||||
.build();
|
||||
instance_builder.build().run(tuple_list!(drcov), state)
|
||||
} else if is_asan && is_cmplog {
|
||||
if let Some(injection_module) = injection_module {
|
||||
instance_builder.build().run(
|
||||
tuple_list!(
|
||||
|
@ -10,14 +10,20 @@ use libafl::events::SimpleEventManager;
|
||||
#[cfg(not(feature = "simplemgr"))]
|
||||
use libafl::events::{EventConfig, Launcher, MonitorTypedEventManager};
|
||||
use libafl::{
|
||||
events::{LlmpEventManager, LlmpRestartingEventManager},
|
||||
monitors::{tui::TuiMonitor, Monitor, MultiMonitor},
|
||||
Error,
|
||||
};
|
||||
#[cfg(feature = "simplemgr")]
|
||||
use libafl_bolts::core_affinity::CoreId;
|
||||
use libafl_bolts::current_time;
|
||||
#[cfg(not(feature = "simplemgr"))]
|
||||
use libafl_bolts::shmem::{ShMemProvider, StdShMemProvider};
|
||||
use libafl_bolts::{
|
||||
current_time,
|
||||
llmp::LlmpBroker,
|
||||
prelude::{CoreId, StateRestorer},
|
||||
tuples::tuple_list,
|
||||
};
|
||||
#[cfg(unix)]
|
||||
use {
|
||||
nix::unistd::dup,
|
||||
@ -82,7 +88,7 @@ impl Fuzzer {
|
||||
{
|
||||
// The shared memory allocator
|
||||
#[cfg(not(feature = "simplemgr"))]
|
||||
let shmem_provider = StdShMemProvider::new()?;
|
||||
let mut shmem_provider = StdShMemProvider::new()?;
|
||||
|
||||
/* If we are running in verbose, don't provide a replacement stdout, otherwise, use /dev/null */
|
||||
#[cfg(not(feature = "simplemgr"))]
|
||||
@ -94,10 +100,42 @@ impl Fuzzer {
|
||||
|
||||
let client = Client::new(&self.options);
|
||||
|
||||
#[cfg(not(feature = "simplemgr"))]
|
||||
if self.options.rerun_input.is_some() {
|
||||
// If we want to rerun a single input but we use a restarting mgr, we'll have to create a fake restarting mgr that doesn't actually restart.
|
||||
// It's not pretty but better than recompiling with simplemgr.
|
||||
|
||||
// Just a random number, let's hope it's free :)
|
||||
let broker_port = 13120;
|
||||
let _fake_broker = LlmpBroker::create_attach_to_tcp(
|
||||
shmem_provider.clone(),
|
||||
tuple_list!(),
|
||||
broker_port,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// To rerun an input, instead of using a launcher, we create dummy parameters and run the client directly.
|
||||
return client.run(
|
||||
None,
|
||||
MonitorTypedEventManager::<_, M>::new(LlmpRestartingEventManager::new(
|
||||
LlmpEventManager::builder()
|
||||
.build_on_port(
|
||||
shmem_provider.clone(),
|
||||
broker_port,
|
||||
EventConfig::AlwaysUnique,
|
||||
None,
|
||||
)
|
||||
.unwrap(),
|
||||
StateRestorer::new(shmem_provider.new_shmem(0x1000).unwrap()),
|
||||
)),
|
||||
CoreId(0),
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "simplemgr")]
|
||||
return client.run(None, SimpleEventManager::new(monitor), CoreId(0));
|
||||
|
||||
// Build and run a Launcher
|
||||
// Build and run the Launcher / fuzzer.
|
||||
#[cfg(not(feature = "simplemgr"))]
|
||||
match Launcher::builder()
|
||||
.shmem_provider(shmem_provider)
|
||||
|
@ -1,5 +1,5 @@
|
||||
use core::{fmt::Debug, ptr::addr_of_mut};
|
||||
use std::{marker::PhantomData, ops::Range, process};
|
||||
use std::{fs, marker::PhantomData, ops::Range, process};
|
||||
|
||||
#[cfg(feature = "simplemgr")]
|
||||
use libafl::events::SimpleEventManager;
|
||||
@ -7,8 +7,8 @@ use libafl::events::SimpleEventManager;
|
||||
use libafl::events::{LlmpRestartingEventManager, MonitorTypedEventManager};
|
||||
use libafl::{
|
||||
corpus::{Corpus, InMemoryOnDiskCorpus, OnDiskCorpus},
|
||||
events::EventRestarter,
|
||||
executors::ShadowExecutor,
|
||||
events::{EventRestarter, NopEventManager},
|
||||
executors::{Executor, ShadowExecutor},
|
||||
feedback_or, feedback_or_fast,
|
||||
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
|
||||
fuzzer::{Evaluator, Fuzzer, StdFuzzer},
|
||||
@ -27,7 +27,7 @@ use libafl::{
|
||||
StagesTuple, StdMutationalStage,
|
||||
},
|
||||
state::{HasCorpus, StdState, UsesState},
|
||||
Error, HasMetadata,
|
||||
Error, HasMetadata, NopFuzzer,
|
||||
};
|
||||
#[cfg(not(feature = "simplemgr"))]
|
||||
use libafl_bolts::shmem::StdShMemProvider;
|
||||
@ -204,6 +204,34 @@ impl<'a, M: Monitor> Instance<'a, M> {
|
||||
|
||||
let emulator = Emulator::empty().qemu(self.qemu).modules(modules).build()?;
|
||||
|
||||
if let Some(rerun_input) = &self.options.rerun_input {
|
||||
// TODO: We might want to support non-bytes inputs at some point?
|
||||
let bytes = fs::read(rerun_input)
|
||||
.unwrap_or_else(|_| panic!("Could not load file {rerun_input:?}"));
|
||||
let input = BytesInput::new(bytes);
|
||||
|
||||
let mut executor = QemuExecutor::new(
|
||||
emulator,
|
||||
&mut harness,
|
||||
observers,
|
||||
&mut fuzzer,
|
||||
&mut state,
|
||||
&mut self.mgr,
|
||||
self.options.timeout,
|
||||
)?;
|
||||
|
||||
executor
|
||||
.run_target(
|
||||
&mut NopFuzzer::new(),
|
||||
&mut state,
|
||||
&mut NopEventManager::new(),
|
||||
&input,
|
||||
)
|
||||
.expect("Error running target");
|
||||
// We're done :)
|
||||
process::exit(0);
|
||||
}
|
||||
|
||||
if self.options.is_cmplog_core(self.core_id) {
|
||||
// Create a QEMU in-process executor
|
||||
let executor = QemuExecutor::new(
|
||||
|
@ -39,7 +39,7 @@ pub struct FuzzerOptions {
|
||||
#[arg(long, help = "Log file")]
|
||||
pub log: Option<String>,
|
||||
|
||||
#[arg(long, help = "Timeout in milli-seconds", default_value = "1000", value_parser = FuzzerOptions::parse_timeout)]
|
||||
#[arg(long, help = "Timeout in milliseconds", default_value = "1000", value_parser = FuzzerOptions::parse_timeout)]
|
||||
pub timeout: Duration,
|
||||
|
||||
#[arg(long = "port", help = "Broker port", default_value_t = 1337_u16)]
|
||||
@ -48,10 +48,10 @@ pub struct FuzzerOptions {
|
||||
#[arg(long, help = "Cpu cores to use", default_value = "all", value_parser = Cores::from_cmdline)]
|
||||
pub cores: Cores,
|
||||
|
||||
#[arg(long, help = "Cpu cores to use for ASAN", value_parser = Cores::from_cmdline)]
|
||||
#[arg(long, help = "Cpu cores to use for ASan", value_parser = Cores::from_cmdline)]
|
||||
pub asan_cores: Option<Cores>,
|
||||
|
||||
#[arg(long, help = "Cpu cores to use for ASAN", value_parser = Cores::from_cmdline)]
|
||||
#[arg(long, help = "Cpu cores to use for ASan", value_parser = Cores::from_cmdline)]
|
||||
pub asan_guest_cores: Option<Cores>,
|
||||
|
||||
#[arg(long, help = "Cpu cores to use for CmpLog", value_parser = Cores::from_cmdline)]
|
||||
@ -63,7 +63,7 @@ pub struct FuzzerOptions {
|
||||
#[clap(long, help = "Enable AFL++ style output", conflicts_with = "verbose")]
|
||||
pub tui: bool,
|
||||
|
||||
#[arg(long = "iterations", help = "Maximum numer of iterations")]
|
||||
#[arg(long = "iterations", help = "Maximum number of iterations")]
|
||||
pub iterations: Option<u64>,
|
||||
|
||||
#[arg(long = "include", help="Include address ranges", value_parser = FuzzerOptions::parse_ranges)]
|
||||
@ -72,6 +72,18 @@ pub struct FuzzerOptions {
|
||||
#[arg(long = "exclude", help="Exclude address ranges", value_parser = FuzzerOptions::parse_ranges, conflicts_with="include")]
|
||||
pub exclude: Option<Vec<Range<GuestAddr>>>,
|
||||
|
||||
#[arg(
|
||||
short = 'd',
|
||||
help = "Write a DrCov Trace for the current input. Requires -r."
|
||||
)]
|
||||
pub drcov: Option<PathBuf>,
|
||||
|
||||
#[arg(
|
||||
short = 'r',
|
||||
help = "An input to rerun, instead of starting to fuzz. Will ignore all other settings apart from -d."
|
||||
)]
|
||||
pub rerun_input: Option<PathBuf>,
|
||||
|
||||
#[arg(last = true, help = "Arguments passed to the target")]
|
||||
pub args: Vec<String>,
|
||||
}
|
||||
@ -182,5 +194,14 @@ impl FuzzerOptions {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if self.drcov.is_some() && self.rerun_input.is_none() {
|
||||
let mut cmd = FuzzerOptions::command();
|
||||
cmd.error(
|
||||
ErrorKind::ValueValidation,
|
||||
format!("The `drcov` option is only supported with `rerun_input`."),
|
||||
)
|
||||
.exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -167,10 +167,8 @@ impl<EMH> LlmpEventManagerBuilder<EMH> {
|
||||
})
|
||||
}
|
||||
|
||||
/// Create an LLMP event manager on a port
|
||||
///
|
||||
/// If the port is not yet bound, it will act as a broker; otherwise, it
|
||||
/// will act as a client.
|
||||
/// Create an LLMP event manager on a port.
|
||||
/// It expects a broker to exist on this port.
|
||||
#[cfg(feature = "std")]
|
||||
pub fn build_on_port<S, SP>(
|
||||
self,
|
||||
|
Loading…
x
Reference in New Issue
Block a user