fix qemu launcher bug (#3206)

* no more shellscript

* metadatas

* clp

* clippo

* fix bug

* taplo

* Merge branch 'qemu_launcher_insane' of github.com:AFLplusplus/LibAFL into qemu_launcher_insane

* fix wrong code
This commit is contained in:
Dongjia "toka" Zhang 2025-05-12 15:12:20 +02:00 committed by GitHub
parent 60c05396da
commit 390008e1d5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 78 additions and 263 deletions

View File

@ -2,9 +2,10 @@ use std::env;
use libafl::{
corpus::{InMemoryOnDiskCorpus, OnDiskCorpus},
events::ClientDescription,
events::{
ClientDescription, EventFirer, EventReceiver, EventRestarter, ProgressReporter, SendExiting,
},
inputs::BytesInput,
monitors::Monitor,
state::StdState,
Error,
};
@ -14,11 +15,7 @@ use libafl_qemu::modules::{
utils::filters::StdAddressFilter, DrCovModule, InjectionModule,
};
use crate::{
harness::Harness,
instance::{ClientMgr, Instance},
options::FuzzerOptions,
};
use crate::{harness::Harness, instance::Instance, options::FuzzerOptions};
#[expect(clippy::module_name_repetitions)]
pub type ClientState =
@ -51,12 +48,19 @@ impl Client<'_> {
}
#[expect(clippy::too_many_lines)]
pub fn run<M: Monitor>(
pub fn run<EM>(
&self,
state: Option<ClientState>,
mgr: ClientMgr<M>,
mgr: EM,
client_description: ClientDescription,
) -> Result<(), Error> {
) -> Result<(), Error>
where
EM: EventFirer<BytesInput, ClientState>
+ EventRestarter<ClientState>
+ ProgressReporter<ClientState>
+ SendExiting
+ EventReceiver<BytesInput, ClientState>,
{
let core_id = client_description.core_id();
let mut args = self.args()?;
Harness::edit_args(&mut args);

View File

@ -5,20 +5,16 @@ use std::{
};
use clap::Parser;
#[cfg(feature = "simplemgr")]
use libafl::events::SimpleEventManager;
#[cfg(not(feature = "simplemgr"))]
use libafl::events::{EventConfig, Launcher, LlmpEventManagerBuilder, MonitorTypedEventManager};
use libafl::events::{EventConfig, Launcher};
use libafl::{
events::ClientDescription,
events::{ClientDescription, SimpleEventManager},
monitors::{tui::TuiMonitor, Monitor, MultiMonitor},
Error,
};
#[cfg(not(feature = "simplemgr"))]
use libafl_bolts::shmem::{ShMemProvider, StdShMemProvider};
use libafl_bolts::{core_affinity::CoreId, current_time};
#[cfg(not(feature = "simplemgr"))]
use libafl_bolts::{llmp::LlmpBroker, staterestore::StateRestorer, tuples::tuple_list};
#[cfg(unix)]
use {
nix::unistd::dup,
@ -84,7 +80,8 @@ impl Fuzzer {
{
// The shared memory allocator
#[cfg(not(feature = "simplemgr"))]
let mut shmem_provider = StdShMemProvider::new()?;
let shmem_provider = StdShMemProvider::new()?;
/* If we are running in verbose, don't provide a replacement stdout, otherwise, use /dev/null */
#[cfg(not(feature = "simplemgr"))]
let stdout = if self.options.verbose {
@ -95,45 +92,10 @@ 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.
// NOTE: This is a hack for debugging that that will only work for non-crashing inputs.
return client.run(
None,
MonitorTypedEventManager::<_, M>::new(
LlmpEventManagerBuilder::builder()
.build_on_port(
shmem_provider.clone(),
broker_port,
EventConfig::AlwaysUnique,
Some(StateRestorer::new(
shmem_provider.new_shmem(0x1000).unwrap(),
)),
)
.unwrap(),
),
ClientDescription::new(0, 0, CoreId(0)),
);
}
#[cfg(feature = "simplemgr")]
if self.options.rerun_input.is_some() {
return client.run(
None,
SimpleEventManager::new(monitor),
SimpleEventManager::new(monitor.clone()),
ClientDescription::new(0, 0, CoreId(0)),
);
}
@ -141,7 +103,7 @@ impl Fuzzer {
#[cfg(feature = "simplemgr")]
return client.run(
None,
SimpleEventManager::new(monitor),
SimpleEventManager::new(monitor.clone()),
ClientDescription::new(0, 0, CoreId(0)),
);
@ -152,7 +114,7 @@ impl Fuzzer {
.broker_port(self.options.port)
.configuration(EventConfig::from_build_id())
.monitor(monitor)
.run_client(|s, m, c| client.run(s, MonitorTypedEventManager::<_, M>::new(m), c))
.run_client(|s, m, c| client.run(s, m, c))
.cores(&self.options.cores)
.stdout_file(stdout)
.stderr_file(stdout)

View File

@ -1,19 +1,16 @@
use core::fmt::Debug;
use std::{fs, marker::PhantomData, ops::Range, process};
use std::{fs, ops::Range, process};
#[cfg(feature = "simplemgr")]
use libafl::events::SimpleEventManager;
#[cfg(not(feature = "simplemgr"))]
use libafl::events::{LlmpRestartingEventManager, MonitorTypedEventManager};
use libafl::{
corpus::{Corpus, HasCurrentCorpusId, InMemoryOnDiskCorpus, OnDiskCorpus},
events::{ClientDescription, EventRestarter},
events::{
ClientDescription, EventFirer, EventReceiver, EventRestarter, ProgressReporter, SendExiting,
},
executors::{Executor, ExitKind, ShadowExecutor},
feedback_and_fast, feedback_or, feedback_or_fast,
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
fuzzer::{Evaluator, Fuzzer, StdFuzzer},
inputs::{BytesInput, Input},
monitors::Monitor,
mutators::{
havoc_mutations, token_mutations::I2SRandReplace, tokens_mutations, HavocScheduledMutator,
StdMOptMutator, Tokens,
@ -31,8 +28,6 @@ use libafl::{
state::{HasCorpus, HasExecutions, HasSolutions, StdState},
Error, HasMetadata,
};
#[cfg(not(feature = "simplemgr"))]
use libafl_bolts::shmem::{StdShMem, StdShMemProvider};
use libafl_bolts::{
ownedref::OwnedMutSlice,
rands::StdRand,
@ -57,14 +52,6 @@ use crate::{harness::Harness, options::FuzzerOptions};
pub type ClientState =
StdState<InMemoryOnDiskCorpus<BytesInput>, BytesInput, StdRand, OnDiskCorpus<BytesInput>>;
#[cfg(feature = "simplemgr")]
pub type ClientMgr<M> = SimpleEventManager<BytesInput, M, ClientState>;
#[cfg(not(feature = "simplemgr"))]
pub type ClientMgr<M> = MonitorTypedEventManager<
LlmpRestartingEventManager<(), BytesInput, ClientState, StdShMem, StdShMemProvider>,
M,
>;
/*
* The snapshot and iterations options interact as follows:
*
@ -87,17 +74,22 @@ pub type ClientMgr<M> = MonitorTypedEventManager<
*/
#[derive(TypedBuilder)]
pub struct Instance<'a, M: Monitor> {
pub struct Instance<'a, EM> {
options: &'a FuzzerOptions,
mgr: ClientMgr<M>,
mgr: EM,
client_description: ClientDescription,
#[builder(default)]
extra_tokens: Vec<String>,
#[builder(default=PhantomData)]
phantom: PhantomData<M>,
}
impl<M: Monitor> Instance<'_, M> {
impl<MTEM> Instance<'_, MTEM>
where
MTEM: EventFirer<BytesInput, ClientState>
+ EventRestarter<ClientState>
+ ProgressReporter<ClientState>
+ SendExiting
+ EventReceiver<BytesInput, ClientState>,
{
fn coverage_filter(&self, qemu: Qemu) -> Result<StdAddressFilter, Error> {
/* Conversion is required on 32-bit targets, but not on 64-bit ones */
if let Some(includes) = &self.options.include {
@ -438,10 +430,10 @@ impl<M: Monitor> Instance<'_, M> {
stages: &mut ST,
) -> Result<(), Error>
where
ST: StagesTuple<E, ClientMgr<M>, ClientState, Z>,
ST: StagesTuple<E, MTEM, ClientState, Z>,
RSM: Fn(&mut E, Qemu),
Z: Fuzzer<E, ClientMgr<M>, BytesInput, ClientState, ST>
+ Evaluator<E, ClientMgr<M>, BytesInput, ClientState>,
Z: Fuzzer<E, MTEM, BytesInput, ClientState, ST>
+ Evaluator<E, MTEM, BytesInput, ClientState>,
{
if state.must_load_initial_inputs() {
let corpus_dirs = [self.options.input_dir()];

View File

@ -1,6 +1,10 @@
use core::marker::PhantomData;
use libafl::{
corpus::{InMemoryOnDiskCorpus, OnDiskCorpus},
events::ClientDescription,
events::{
ClientDescription, EventFirer, EventReceiver, EventRestarter, ProgressReporter, SendExiting,
},
inputs::BytesInput,
monitors::Monitor,
state::StdState,
@ -8,11 +12,7 @@ use libafl::{
};
use libafl_bolts::rands::StdRand;
use crate::{
instance::{ClientMgr, Instance},
options::FuzzerOptions,
};
use crate::{instance::Instance, options::FuzzerOptions};
#[allow(clippy::module_name_repetitions)]
pub type ClientState =
StdState<InMemoryOnDiskCorpus<BytesInput>, BytesInput, StdRand, OnDiskCorpus<BytesInput>>;
@ -26,12 +26,19 @@ impl Client<'_> {
Client { options }
}
pub fn run<M: Monitor>(
pub fn run<EM>(
&self,
state: Option<ClientState>,
mgr: ClientMgr<M>,
mgr: EM,
client_description: ClientDescription,
) -> Result<(), Error> {
) -> Result<(), Error>
where
EM: EventFirer<BytesInput, ClientState>
+ EventRestarter<ClientState>
+ ProgressReporter<ClientState>
+ SendExiting
+ EventReceiver<BytesInput, ClientState>,
{
let instance = Instance::builder()
.options(self.options)
.mgr(mgr)

View File

@ -7,7 +7,7 @@ use std::{
use clap::Parser;
use libafl::{
events::{
ClientDescription, EventConfig, Launcher, LlmpEventManagerBuilder, MonitorTypedEventManager,
ClientDescription, EventConfig, Launcher, LlmpEventManagerBuilder, SimpleEventManager,
},
monitors::{tui::TuiMonitor, Monitor, MultiMonitor},
Error,
@ -83,7 +83,7 @@ impl Fuzzer {
M: Monitor + Clone,
{
// The shared memory allocator
let mut shmem_provider = StdShMemProvider::new()?;
let shmem_provider = StdShMemProvider::new()?;
/* If we are running in verbose, don't provide a replacement stdout, otherwise, use /dev/null */
let stdout = if self.options.verbose {
@ -95,45 +95,28 @@ impl Fuzzer {
let client = Client::new(&self.options);
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(
LlmpEventManagerBuilder::builder().build_on_port(
shmem_provider.clone(),
broker_port,
EventConfig::AlwaysUnique,
Some(StateRestorer::new(
shmem_provider.new_shmem(0x1000).unwrap(),
)),
)?,
),
SimpleEventManager::new(monitor.clone()),
ClientDescription::new(0, 0, CoreId(0)),
);
}
#[cfg(feature = "simplemgr")]
return client.run(None, SimpleEventManager::new(monitor), CoreId(0));
return client.run(
None,
SimpleEventManager::new(monitor.clone()),
ClientDescription::new(0, 0, CoreId(0)),
);
// Build and run a Launcher
#[cfg(not(feature = "simplemgr"))]
match Launcher::builder()
.shmem_provider(shmem_provider)
.broker_port(self.options.port)
.configuration(EventConfig::from_build_id())
.monitor(monitor)
.run_client(|s, m, c| client.run(s, MonitorTypedEventManager::<_, M>::new(m), c))
.run_client(|s, m, c| client.run(s, m, c))
.cores(&self.options.cores)
.stdout_file(stdout)
.stderr_file(stdout)

View File

@ -3,15 +3,14 @@ use std::{marker::PhantomData, process};
use libafl::{
corpus::{Corpus, InMemoryOnDiskCorpus, OnDiskCorpus},
events::{
ClientDescription, EventRestarter, LlmpRestartingEventManager, MonitorTypedEventManager,
NopEventManager,
ClientDescription, EventFirer, EventReceiver, EventRestarter, LlmpRestartingEventManager,
NopEventManager, ProgressReporter, SendExiting,
},
executors::{Executor, ShadowExecutor},
feedback_and_fast, feedback_or, feedback_or_fast,
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
fuzzer::{Evaluator, Fuzzer, StdFuzzer},
inputs::BytesInput,
monitors::Monitor,
mutators::{
havoc_mutations, tokens_mutations, HavocScheduledMutator, I2SRandReplace, StdMOptMutator,
Tokens,
@ -43,22 +42,22 @@ use crate::options::FuzzerOptions;
pub type ClientState =
StdState<InMemoryOnDiskCorpus<BytesInput>, BytesInput, StdRand, OnDiskCorpus<BytesInput>>;
pub type ClientMgr<M> = MonitorTypedEventManager<
LlmpRestartingEventManager<(), BytesInput, ClientState, StdShMem, StdShMemProvider>,
M,
>;
#[derive(TypedBuilder)]
pub struct Instance<'a, M: Monitor> {
pub struct Instance<'a, EM> {
options: &'a FuzzerOptions,
/// The harness. We create it before forking, then `take()` it inside the client.
mgr: ClientMgr<M>,
mgr: EM,
client_description: ClientDescription,
#[builder(default=PhantomData)]
phantom: PhantomData<M>,
}
impl<M: Monitor> Instance<'_, M> {
impl<EM> Instance<'_, EM>
where
EM: EventFirer<BytesInput, ClientState>
+ EventRestarter<ClientState>
+ ProgressReporter<ClientState>
+ SendExiting
+ EventReceiver<BytesInput, ClientState>,
{
pub fn run(mut self, state: Option<ClientState>) -> Result<(), Error> {
let parent_cpu_id = self
.options
@ -229,9 +228,8 @@ impl<M: Monitor> Instance<'_, M> {
stages: &mut ST,
) -> Result<(), Error>
where
Z: Fuzzer<E, ClientMgr<M>, BytesInput, ClientState, ST>
+ Evaluator<E, ClientMgr<M>, BytesInput, ClientState>,
ST: StagesTuple<E, ClientMgr<M>, ClientState, Z>,
Z: Fuzzer<E, EM, BytesInput, ClientState, ST> + Evaluator<E, EM, BytesInput, ClientState>,
ST: StagesTuple<E, EM, ClientState, Z>,
{
let corpus_dirs = [self.options.input_dir()];

View File

@ -697,137 +697,6 @@ impl HasEventManagerId for NopEventManager {
}
}
/// An `EventManager` type that wraps another manager, but captures a `monitor` type as well.
/// This is useful to keep the same API between managers with and without an internal `monitor`.
#[derive(Copy, Clone, Debug)]
pub struct MonitorTypedEventManager<EM, M> {
inner: EM,
phantom: PhantomData<M>,
}
impl<EM, M> MonitorTypedEventManager<EM, M> {
/// Creates a new `EventManager` that wraps another manager, but captures a `monitor` type as well.
#[must_use]
pub fn new(inner: EM) -> Self {
MonitorTypedEventManager {
inner,
phantom: PhantomData,
}
}
}
impl<EM, I, M, S> EventFirer<I, S> for MonitorTypedEventManager<EM, M>
where
EM: EventFirer<I, S>,
{
fn should_send(&self) -> bool {
true
}
#[inline]
fn fire(&mut self, state: &mut S, event: EventWithStats<I>) -> Result<(), Error> {
self.inner.fire(state, event)
}
#[inline]
fn log(
&mut self,
state: &mut S,
severity_level: LogSeverity,
message: String,
) -> Result<(), Error>
where
S: HasExecutions,
{
self.inner.log(state, severity_level, message)
}
#[inline]
fn configuration(&self) -> EventConfig {
self.inner.configuration()
}
}
impl<EM, M, S> EventRestarter<S> for MonitorTypedEventManager<EM, M>
where
EM: EventRestarter<S>,
{
#[inline]
fn on_restart(&mut self, state: &mut S) -> Result<(), Error> {
self.inner.on_restart(state)
}
}
impl<EM, M> SendExiting for MonitorTypedEventManager<EM, M>
where
EM: SendExiting,
{
#[inline]
fn send_exiting(&mut self) -> Result<(), Error> {
self.inner.send_exiting()
}
fn on_shutdown(&mut self) -> Result<(), Error> {
self.inner.on_shutdown()
}
}
impl<EM, M> AwaitRestartSafe for MonitorTypedEventManager<EM, M>
where
EM: AwaitRestartSafe,
{
#[inline]
fn await_restart_safe(&mut self) {
self.inner.await_restart_safe();
}
}
impl<EM, I, M, S> EventReceiver<I, S> for MonitorTypedEventManager<EM, M>
where
EM: EventReceiver<I, S>,
{
#[inline]
fn try_receive(&mut self, state: &mut S) -> Result<Option<(EventWithStats<I>, bool)>, Error> {
self.inner.try_receive(state)
}
fn on_interesting(
&mut self,
_state: &mut S,
_event_vec: EventWithStats<I>,
) -> Result<(), Error> {
Ok(())
}
}
impl<EM, M, S> ProgressReporter<S> for MonitorTypedEventManager<EM, M>
where
EM: ProgressReporter<S>,
{
#[inline]
fn maybe_report_progress(
&mut self,
state: &mut S,
monitor_timeout: Duration,
) -> Result<(), Error> {
self.inner.maybe_report_progress(state, monitor_timeout)
}
#[inline]
fn report_progress(&mut self, state: &mut S) -> Result<(), Error> {
self.inner.report_progress(state)
}
}
impl<EM, M> HasEventManagerId for MonitorTypedEventManager<EM, M>
where
EM: HasEventManagerId,
{
#[inline]
fn mgr_id(&self) -> EventManagerId {
self.inner.mgr_id()
}
}
#[cfg(test)]
mod tests {