Refactor Output Observers (#856)
* Refactor Output Observers * Delete .gitmodules * modules * Drop need for OutputObserving list
This commit is contained in:
parent
5b75b6b8ac
commit
9695ce0029
@ -24,7 +24,7 @@ use libafl::{
|
|||||||
inputs::{HasTargetBytes, Input},
|
inputs::{HasTargetBytes, Input},
|
||||||
monitors::SimpleMonitor,
|
monitors::SimpleMonitor,
|
||||||
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
|
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
|
||||||
observers::{get_asan_runtime_flags, ASANBacktraceObserver, StdMapObserver},
|
observers::{get_asan_runtime_flags, AsanBacktraceObserver, StdMapObserver},
|
||||||
schedulers::QueueScheduler,
|
schedulers::QueueScheduler,
|
||||||
stages::mutational::StdMutationalStage,
|
stages::mutational::StdMutationalStage,
|
||||||
state::StdState,
|
state::StdState,
|
||||||
@ -40,7 +40,7 @@ pub fn main() {
|
|||||||
// Create an observation channel using the signals map
|
// Create an observation channel using the signals map
|
||||||
let observer = StdMapObserver::new("signals", signals.as_mut_slice());
|
let observer = StdMapObserver::new("signals", signals.as_mut_slice());
|
||||||
// Create a stacktrace observer
|
// Create a stacktrace observer
|
||||||
let bt_observer = ASANBacktraceObserver::new("ASANBacktraceObserver");
|
let bt_observer = AsanBacktraceObserver::new("AsanBacktraceObserver");
|
||||||
|
|
||||||
// Feedback to rate the interestingness of an input, obtained by ANDing the interestingness of both feedbacks
|
// Feedback to rate the interestingness of an input, obtained by ANDing the interestingness of both feedbacks
|
||||||
let mut feedback = MaxMapFeedback::new(&observer);
|
let mut feedback = MaxMapFeedback::new(&observer);
|
||||||
@ -48,7 +48,7 @@ pub fn main() {
|
|||||||
// A feedback to choose if an input is a solution or not
|
// A feedback to choose if an input is a solution or not
|
||||||
let mut objective = feedback_and!(
|
let mut objective = feedback_and!(
|
||||||
CrashFeedback::new(),
|
CrashFeedback::new(),
|
||||||
NewHashFeedback::<ASANBacktraceObserver>::new(&bt_observer)
|
NewHashFeedback::<AsanBacktraceObserver>::new(&bt_observer)
|
||||||
);
|
);
|
||||||
// let mut objective = CrashFeedback::new();
|
// let mut objective = CrashFeedback::new();
|
||||||
|
|
||||||
@ -76,7 +76,7 @@ pub fn main() {
|
|||||||
// such as the notification of the addition of a new item to the corpus
|
// such as the notification of the addition of a new item to the corpus
|
||||||
let mut mgr = SimpleEventManager::new(mon);
|
let mut mgr = SimpleEventManager::new(mon);
|
||||||
|
|
||||||
// A queue policy to get testcasess from the corpus
|
// A queue policy to get testcases from the corpus
|
||||||
let scheduler = QueueScheduler::new();
|
let scheduler = QueueScheduler::new();
|
||||||
|
|
||||||
// A fuzzer with feedbacks and a corpus scheduler
|
// A fuzzer with feedbacks and a corpus scheduler
|
||||||
@ -108,12 +108,7 @@ pub fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut executor = MyExecutor { shmem_id }.into_executor(
|
let mut executor = MyExecutor { shmem_id }.into_executor(tuple_list!(observer, bt_observer));
|
||||||
tuple_list!(observer, bt_observer),
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
Some("ASANBacktraceObserver".to_string()),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Generator of printable bytearrays of max size 32
|
// Generator of printable bytearrays of max size 32
|
||||||
let mut generator = RandPrintablesGenerator::new(32);
|
let mut generator = RandPrintablesGenerator::new(32);
|
||||||
|
@ -22,7 +22,7 @@ use libafl::{
|
|||||||
inputs::BytesInput,
|
inputs::BytesInput,
|
||||||
monitors::SimpleMonitor,
|
monitors::SimpleMonitor,
|
||||||
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
|
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
|
||||||
observers::{ASANBacktraceObserver, ConstMapObserver, HitcountsMapObserver},
|
observers::{AsanBacktraceObserver, ConstMapObserver, HitcountsMapObserver},
|
||||||
schedulers::QueueScheduler,
|
schedulers::QueueScheduler,
|
||||||
stages::mutational::StdMutationalStage,
|
stages::mutational::StdMutationalStage,
|
||||||
state::StdState,
|
state::StdState,
|
||||||
@ -50,7 +50,7 @@ pub fn main() {
|
|||||||
shmem_map,
|
shmem_map,
|
||||||
));
|
));
|
||||||
|
|
||||||
let bt_observer = ASANBacktraceObserver::new("ASANBacktraceObserver");
|
let bt_observer = AsanBacktraceObserver::new("AsanBacktraceObserver");
|
||||||
|
|
||||||
// Feedback to rate the interestingness of an input
|
// Feedback to rate the interestingness of an input
|
||||||
// This one is composed by two Feedbacks in OR
|
// This one is composed by two Feedbacks in OR
|
||||||
|
@ -224,7 +224,7 @@ fn fuzz(
|
|||||||
// Create a concolic trace
|
// Create a concolic trace
|
||||||
ConcolicTracingStage::new(
|
ConcolicTracingStage::new(
|
||||||
TracingStage::new(
|
TracingStage::new(
|
||||||
MyCommandConfigurator::default().into_executor(tuple_list!(concolic_observer), None, None, None)
|
MyCommandConfigurator::default().into_executor(tuple_list!(concolic_observer))
|
||||||
),
|
),
|
||||||
concolic_observer_name,
|
concolic_observer_name,
|
||||||
),
|
),
|
||||||
|
@ -36,7 +36,7 @@ pub fn read_time_counter() -> u64 {
|
|||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn read_time_counter() -> u64 {
|
pub fn read_time_counter() -> u64 {
|
||||||
let mut v: u64 = 0;
|
let mut v: u64;
|
||||||
unsafe {
|
unsafe {
|
||||||
// TODO pushing a change in core::arch::aarch64 ?
|
// TODO pushing a change in core::arch::aarch64 ?
|
||||||
asm!("mrs {v}, cntvct_el0", v = out(reg) v);
|
asm!("mrs {v}, cntvct_el0", v = out(reg) v);
|
||||||
|
@ -27,7 +27,7 @@ use crate::{
|
|||||||
AsSlice,
|
AsSlice,
|
||||||
},
|
},
|
||||||
inputs::HasTargetBytes,
|
inputs::HasTargetBytes,
|
||||||
observers::{ASANBacktraceObserver, ObserversTuple, StdErrObserver, StdOutObserver},
|
observers::ObserversTuple,
|
||||||
std::borrow::ToOwned,
|
std::borrow::ToOwned,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
@ -45,10 +45,10 @@ enum InputLocation {
|
|||||||
},
|
},
|
||||||
/// Deliver input via `StdIn`
|
/// Deliver input via `StdIn`
|
||||||
StdIn,
|
StdIn,
|
||||||
/// Deliver the iniput via the specified [`InputFile`]
|
/// Deliver the input via the specified [`InputFile`]
|
||||||
/// You can use specify [`InputFile::create(INPUTFILE_STD)`] to use a default filename.
|
/// You can use specify [`InputFile::create(INPUTFILE_STD)`] to use a default filename.
|
||||||
File {
|
File {
|
||||||
/// The fiel to write input to. The target should read input from this location.
|
/// The file to write input to. The target should read input from this location.
|
||||||
out_file: InputFile,
|
out_file: InputFile,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -79,7 +79,6 @@ pub struct StdCommandConfigurator {
|
|||||||
debug_child: bool,
|
debug_child: bool,
|
||||||
has_stdout_observer: bool,
|
has_stdout_observer: bool,
|
||||||
has_stderr_observer: bool,
|
has_stderr_observer: bool,
|
||||||
has_asan_observer: bool,
|
|
||||||
/// true: input gets delivered via stdink
|
/// true: input gets delivered via stdink
|
||||||
input_location: InputLocation,
|
input_location: InputLocation,
|
||||||
/// The Command to execute
|
/// The Command to execute
|
||||||
@ -104,7 +103,7 @@ impl CommandConfigurator for StdCommandConfigurator {
|
|||||||
if self.has_stdout_observer {
|
if self.has_stdout_observer {
|
||||||
cmd.stdout(Stdio::piped());
|
cmd.stdout(Stdio::piped());
|
||||||
}
|
}
|
||||||
if self.has_stderr_observer || self.has_asan_observer {
|
if self.has_stderr_observer {
|
||||||
cmd.stderr(Stdio::piped());
|
cmd.stderr(Stdio::piped());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,23 +149,16 @@ impl CommandConfigurator for StdCommandConfigurator {
|
|||||||
|
|
||||||
/// A `CommandExecutor` is a wrapper around [`std::process::Command`] to execute a target as a child process.
|
/// A `CommandExecutor` is a wrapper around [`std::process::Command`] to execute a target as a child process.
|
||||||
/// Construct a `CommandExecutor` by implementing [`CommandConfigurator`] for a type of your choice and calling [`CommandConfigurator::into_executor`] on it.
|
/// Construct a `CommandExecutor` by implementing [`CommandConfigurator`] for a type of your choice and calling [`CommandConfigurator::into_executor`] on it.
|
||||||
/// Instead, you can use [`CommandExecutor::builder()`] to construct a [`CommandExecutor`] backed by a [`StdCommandConfigurator`].
|
/// Instead, you can also use [`CommandExecutor::builder()`] to construct a [`CommandExecutor`] backed by a [`StdCommandConfigurator`].
|
||||||
pub struct CommandExecutor<EM, I, OT, S, T, Z>
|
pub struct CommandExecutor<EM, I, OT, S, T, Z>
|
||||||
where
|
where
|
||||||
T: Debug,
|
T: Debug,
|
||||||
OT: Debug,
|
OT: Debug,
|
||||||
{
|
{
|
||||||
/// The wrapped comand configurer
|
/// The wrapped command configurer
|
||||||
configurer: T,
|
configurer: T,
|
||||||
|
/// The obsevers used by this executor
|
||||||
observers: OT,
|
observers: OT,
|
||||||
/// cache if the AsanBacktraceObserver is present
|
|
||||||
has_asan_observer: Option<String>,
|
|
||||||
/// If set, we found a [`StdErrObserver`] in the observer list.
|
|
||||||
/// Pipe the child's `stderr` instead of closing it.
|
|
||||||
has_stdout_observer: Option<String>,
|
|
||||||
/// If set, we found a [`StdOutObserver`] in the observer list
|
|
||||||
/// Pipe the child's `stdout` instead of closing it.
|
|
||||||
has_stderr_observer: Option<String>,
|
|
||||||
phantom: PhantomData<(EM, I, S, Z)>,
|
phantom: PhantomData<(EM, I, S, Z)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,18 +207,15 @@ where
|
|||||||
|
|
||||||
impl<EM, I, OT, S, Z> CommandExecutor<EM, I, OT, S, StdCommandConfigurator, Z>
|
impl<EM, I, OT, S, Z> CommandExecutor<EM, I, OT, S, StdCommandConfigurator, Z>
|
||||||
where
|
where
|
||||||
OT: MatchName + Debug,
|
OT: MatchName + Debug + ObserversTuple<I, S>,
|
||||||
{
|
{
|
||||||
/// Creates a new `CommandExecutor`.
|
/// Creates a new `CommandExecutor`.
|
||||||
/// Instead of parsing the Command for `@@`, it will
|
/// Instead of parsing the Command for `@@`, it will
|
||||||
pub fn from_cmd_with_file<P>(
|
pub fn from_cmd_with_file<P>(
|
||||||
cmd: &Command,
|
cmd: &Command,
|
||||||
debug_child: bool,
|
debug_child: bool,
|
||||||
observers: OT,
|
mut observers: OT,
|
||||||
path: P,
|
path: P,
|
||||||
has_stdout_observer: Option<String>,
|
|
||||||
has_stderr_observer: Option<String>,
|
|
||||||
has_asan_observer: Option<String>,
|
|
||||||
) -> Result<Self, Error>
|
) -> Result<Self, Error>
|
||||||
where
|
where
|
||||||
P: AsRef<Path>,
|
P: AsRef<Path>,
|
||||||
@ -238,43 +227,37 @@ where
|
|||||||
}
|
}
|
||||||
command.stdin(Stdio::null());
|
command.stdin(Stdio::null());
|
||||||
|
|
||||||
if has_stdout_observer.is_some() {
|
let has_stdout_observer = observers.observes_stdout();
|
||||||
|
if has_stdout_observer {
|
||||||
command.stdout(Stdio::piped());
|
command.stdout(Stdio::piped());
|
||||||
}
|
}
|
||||||
if has_stderr_observer.is_some() || has_asan_observer.is_some() {
|
let has_stderr_observer = observers.observes_stderr();
|
||||||
|
if has_stderr_observer {
|
||||||
command.stderr(Stdio::piped());
|
command.stderr(Stdio::piped());
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
observers,
|
observers,
|
||||||
|
|
||||||
configurer: StdCommandConfigurator {
|
configurer: StdCommandConfigurator {
|
||||||
input_location: InputLocation::File {
|
input_location: InputLocation::File {
|
||||||
out_file: InputFile::create(path)?,
|
out_file: InputFile::create(path)?,
|
||||||
},
|
},
|
||||||
command,
|
command,
|
||||||
debug_child,
|
debug_child,
|
||||||
has_stdout_observer: has_stdout_observer.is_some(),
|
|
||||||
has_stderr_observer: has_stderr_observer.is_some(),
|
|
||||||
has_asan_observer: has_asan_observer.is_some(),
|
|
||||||
},
|
|
||||||
has_stdout_observer,
|
has_stdout_observer,
|
||||||
has_stderr_observer,
|
has_stderr_observer,
|
||||||
has_asan_observer,
|
},
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses an AFL-like comandline, replacing `@@` with the input file.
|
/// Parses an AFL-like commandline, replacing `@@` with the input file.
|
||||||
/// If no `@@` was found, will use stdin for input.
|
/// If no `@@` was found, will use stdin for input.
|
||||||
/// The arg 0 is the program.
|
/// The arg 0 is the program.
|
||||||
pub fn parse_afl_cmdline<IT, O>(
|
pub fn parse_afl_cmdline<IT, O>(
|
||||||
args: IT,
|
args: IT,
|
||||||
observers: OT,
|
observers: OT,
|
||||||
debug_child: bool,
|
debug_child: bool,
|
||||||
has_stdout_observer: Option<String>,
|
|
||||||
has_stderr_observer: Option<String>,
|
|
||||||
has_asan_observer: Option<String>,
|
|
||||||
) -> Result<Self, Error>
|
) -> Result<Self, Error>
|
||||||
where
|
where
|
||||||
IT: IntoIterator<Item = O>,
|
IT: IntoIterator<Item = O>,
|
||||||
@ -283,16 +266,6 @@ where
|
|||||||
let mut atat_at = None;
|
let mut atat_at = None;
|
||||||
let mut builder = CommandExecutorBuilder::new();
|
let mut builder = CommandExecutorBuilder::new();
|
||||||
builder.debug_child(debug_child);
|
builder.debug_child(debug_child);
|
||||||
if let Some(name) = has_stdout_observer {
|
|
||||||
builder.stdout_observer(name);
|
|
||||||
}
|
|
||||||
if let Some(name) = has_stderr_observer {
|
|
||||||
builder.stderr_observer(name);
|
|
||||||
}
|
|
||||||
if let Some(name) = has_asan_observer {
|
|
||||||
builder.asan_observer(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
let afl_delim = OsStr::new("@@");
|
let afl_delim = OsStr::new("@@");
|
||||||
|
|
||||||
for (pos, arg) in args.into_iter().enumerate() {
|
for (pos, arg) in args.into_iter().enumerate() {
|
||||||
@ -326,7 +299,7 @@ impl<EM, I, OT, S, T, Z> Executor<EM, I, S, Z> for CommandExecutor<EM, I, OT, S,
|
|||||||
where
|
where
|
||||||
I: Input + HasTargetBytes,
|
I: Input + HasTargetBytes,
|
||||||
T: CommandConfigurator,
|
T: CommandConfigurator,
|
||||||
OT: Debug + MatchName,
|
OT: Debug + MatchName + ObserversTuple<I, S>,
|
||||||
T: Debug,
|
T: Debug,
|
||||||
{
|
{
|
||||||
fn run_target(
|
fn run_target(
|
||||||
@ -361,37 +334,23 @@ where
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if self.has_asan_observer.is_some() || self.has_stderr_observer.is_some() {
|
if self.observers.observes_stderr() {
|
||||||
let mut stderr = String::new();
|
let mut stderr = String::new();
|
||||||
child.stderr.as_mut().ok_or_else(|| {
|
child.stderr.as_mut().ok_or_else(|| {
|
||||||
Error::illegal_state(
|
Error::illegal_state(
|
||||||
"Observer tries to read stderr, but stderr was not `Stdio::pipe` in CommandExecutor",
|
"Observer tries to read stderr, but stderr was not `Stdio::pipe` in CommandExecutor",
|
||||||
)
|
)
|
||||||
})?.read_to_string(&mut stderr)?;
|
})?.read_to_string(&mut stderr)?;
|
||||||
if let Some(name) = self.has_asan_observer.as_ref() {
|
self.observers.observe_stderr(&stderr);
|
||||||
self.observers
|
|
||||||
.match_name_mut::<ASANBacktraceObserver>(name)
|
|
||||||
.unwrap()
|
|
||||||
.parse_asan_output(&stderr);
|
|
||||||
}
|
}
|
||||||
if let Some(name) = self.has_stderr_observer.as_ref() {
|
if self.observers.observes_stdout() {
|
||||||
self.observers
|
|
||||||
.match_name_mut::<StdErrObserver>(name)
|
|
||||||
.unwrap()
|
|
||||||
.stderr = Some(stderr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(name) = self.has_stdout_observer.as_ref() {
|
|
||||||
let mut stdout = String::new();
|
let mut stdout = String::new();
|
||||||
child.stdout.as_mut().ok_or_else(|| {
|
child.stdout.as_mut().ok_or_else(|| {
|
||||||
Error::illegal_state(
|
Error::illegal_state(
|
||||||
"Observer tries to read stdout, but stdout was not `Stdio::pipe` in CommandExecutor",
|
"Observer tries to read stdout, but stdout was not `Stdio::pipe` in CommandExecutor",
|
||||||
)
|
)
|
||||||
})?.read_to_string(&mut stdout)?;
|
})?.read_to_string(&mut stdout)?;
|
||||||
self.observers
|
self.observers.observe_stdout(&stdout);
|
||||||
.match_name_mut::<StdOutObserver>(name)
|
|
||||||
.unwrap()
|
|
||||||
.stdout = Some(stdout);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
res
|
res
|
||||||
@ -419,9 +378,6 @@ pub struct CommandExecutorBuilder {
|
|||||||
input_location: InputLocation,
|
input_location: InputLocation,
|
||||||
cwd: Option<PathBuf>,
|
cwd: Option<PathBuf>,
|
||||||
envs: Vec<(OsString, OsString)>,
|
envs: Vec<(OsString, OsString)>,
|
||||||
has_stdout_observer: Option<String>,
|
|
||||||
has_stderr_observer: Option<String>,
|
|
||||||
has_asan_observer: Option<String>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for CommandExecutorBuilder {
|
impl Default for CommandExecutorBuilder {
|
||||||
@ -441,30 +397,9 @@ impl CommandExecutorBuilder {
|
|||||||
cwd: None,
|
cwd: None,
|
||||||
envs: vec![],
|
envs: vec![],
|
||||||
debug_child: false,
|
debug_child: false,
|
||||||
has_stdout_observer: None,
|
|
||||||
has_stderr_observer: None,
|
|
||||||
has_asan_observer: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the stdout observer name
|
|
||||||
pub fn stdout_observer(&mut self, name: String) -> &mut Self {
|
|
||||||
self.has_stdout_observer = Some(name);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the stderr observer name
|
|
||||||
pub fn stderr_observer(&mut self, name: String) -> &mut Self {
|
|
||||||
self.has_stderr_observer = Some(name);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the asan observer name
|
|
||||||
pub fn asan_observer(&mut self, name: String) -> &mut Self {
|
|
||||||
self.has_asan_observer = Some(name);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the binary to execute
|
/// Set the binary to execute
|
||||||
/// This option is required.
|
/// This option is required.
|
||||||
pub fn program<O>(&mut self, program: O) -> &mut Self
|
pub fn program<O>(&mut self, program: O) -> &mut Self
|
||||||
@ -573,13 +508,13 @@ impl CommandExecutorBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Builds the `ComandExecutor`
|
/// Builds the `CommandExecutor`.
|
||||||
pub fn build<EM, I, OT, S, Z>(
|
pub fn build<EM, I, OT, S, Z>(
|
||||||
&self,
|
&self,
|
||||||
observers: OT,
|
mut observers: OT,
|
||||||
) -> Result<CommandExecutor<EM, I, OT, S, StdCommandConfigurator, Z>, Error>
|
) -> Result<CommandExecutor<EM, I, OT, S, StdCommandConfigurator, Z>, Error>
|
||||||
where
|
where
|
||||||
OT: Debug + MatchName,
|
OT: Debug + MatchName + ObserversTuple<I, S>,
|
||||||
{
|
{
|
||||||
let program = if let Some(program) = &self.program {
|
let program = if let Some(program) = &self.program {
|
||||||
program
|
program
|
||||||
@ -610,28 +545,22 @@ impl CommandExecutorBuilder {
|
|||||||
command.stdout(Stdio::null());
|
command.stdout(Stdio::null());
|
||||||
command.stderr(Stdio::null());
|
command.stderr(Stdio::null());
|
||||||
}
|
}
|
||||||
if self.has_stderr_observer.is_some() || self.has_asan_observer.is_some() {
|
if observers.observes_stdout() {
|
||||||
// we need stderr for ASANBackt
|
|
||||||
command.stderr(Stdio::piped());
|
|
||||||
}
|
|
||||||
if self.has_stdout_observer.is_some() {
|
|
||||||
command.stdout(Stdio::piped());
|
command.stdout(Stdio::piped());
|
||||||
}
|
}
|
||||||
|
if observers.observes_stderr() {
|
||||||
|
// we need stderr for `AsanBacktaceObserver`, and others
|
||||||
|
command.stderr(Stdio::piped());
|
||||||
|
}
|
||||||
|
|
||||||
let configurator = StdCommandConfigurator {
|
let configurator = StdCommandConfigurator {
|
||||||
debug_child: self.debug_child,
|
debug_child: self.debug_child,
|
||||||
has_stdout_observer: self.has_stdout_observer.is_some(),
|
has_stdout_observer: observers.observes_stdout(),
|
||||||
has_stderr_observer: self.has_stderr_observer.is_some(),
|
has_stderr_observer: observers.observes_stderr(),
|
||||||
has_asan_observer: self.has_asan_observer.is_some(),
|
|
||||||
input_location: self.input_location.clone(),
|
input_location: self.input_location.clone(),
|
||||||
command,
|
command,
|
||||||
};
|
};
|
||||||
Ok(configurator.into_executor::<EM, I, OT, S, Z>(
|
Ok(configurator.into_executor::<EM, I, OT, S, Z>(observers))
|
||||||
observers,
|
|
||||||
self.has_stdout_observer.clone(),
|
|
||||||
self.has_stderr_observer.clone(),
|
|
||||||
self.has_asan_observer.clone(),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -663,7 +592,7 @@ impl CommandExecutorBuilder {
|
|||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// fn make_executor<EM, I: Input + HasTargetBytes, S, Z>() -> impl Executor<EM, I, S, Z> {
|
/// fn make_executor<EM, I: Input + HasTargetBytes, S, Z>() -> impl Executor<EM, I, S, Z> {
|
||||||
/// MyExecutor.into_executor((), None, None, None)
|
/// MyExecutor.into_executor(())
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
@ -675,21 +604,13 @@ pub trait CommandConfigurator: Sized + Debug {
|
|||||||
I: Input + HasTargetBytes;
|
I: Input + HasTargetBytes;
|
||||||
|
|
||||||
/// Create an `Executor` from this `CommandConfigurator`.
|
/// Create an `Executor` from this `CommandConfigurator`.
|
||||||
fn into_executor<EM, I, OT, S, Z>(
|
/// It will observe the outputs with the respective given observer name.
|
||||||
self,
|
fn into_executor<EM, I, OT, S, Z>(self, observers: OT) -> CommandExecutor<EM, I, OT, S, Self, Z>
|
||||||
observers: OT,
|
|
||||||
has_stdout_observer: Option<String>,
|
|
||||||
has_stderr_observer: Option<String>,
|
|
||||||
has_asan_observer: Option<String>,
|
|
||||||
) -> CommandExecutor<EM, I, OT, S, Self, Z>
|
|
||||||
where
|
where
|
||||||
OT: Debug + MatchName,
|
OT: Debug + MatchName,
|
||||||
{
|
{
|
||||||
CommandExecutor {
|
CommandExecutor {
|
||||||
observers,
|
observers,
|
||||||
has_asan_observer,
|
|
||||||
has_stdout_observer,
|
|
||||||
has_stderr_observer,
|
|
||||||
configurer: self,
|
configurer: self,
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
}
|
}
|
||||||
@ -740,14 +661,8 @@ mod tests {
|
|||||||
println!("{status}");
|
println!("{status}");
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let mut executor = CommandExecutor::parse_afl_cmdline(
|
let mut executor =
|
||||||
&["file".to_string(), "@@".to_string()],
|
CommandExecutor::parse_afl_cmdline(&["file".to_string(), "@@".to_string()], (), true)
|
||||||
(),
|
|
||||||
true,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
executor
|
executor
|
||||||
.run_target(
|
.run_target(
|
||||||
|
@ -146,6 +146,29 @@ where
|
|||||||
.as_mut()
|
.as_mut()
|
||||||
.post_exec_child_all(state, input, exit_kind)
|
.post_exec_child_all(state, input, exit_kind)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if a `stdout` observer was added to the list
|
||||||
|
#[inline]
|
||||||
|
fn observes_stdout(&mut self) -> bool {
|
||||||
|
self.primary.as_mut().observes_stdout() || self.secondary.as_mut().observes_stdout()
|
||||||
|
}
|
||||||
|
/// Returns true if a `stderr` observer was added to the list
|
||||||
|
#[inline]
|
||||||
|
fn observes_stderr(&mut self) -> bool {
|
||||||
|
self.primary.as_mut().observes_stderr() || self.secondary.as_mut().observes_stderr()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Runs `observe_stdout` for all stdout observers in the list
|
||||||
|
fn observe_stdout(&mut self, stdout: &str) {
|
||||||
|
self.primary.as_mut().observe_stderr(stdout);
|
||||||
|
self.secondary.as_mut().observe_stderr(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Runs `observe_stderr` for all stderr observers in the list
|
||||||
|
fn observe_stderr(&mut self, stderr: &str) {
|
||||||
|
self.primary.as_mut().observe_stderr(stderr);
|
||||||
|
self.secondary.as_mut().observe_stderr(stderr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A, B> MatchName for ProxyObserversTuple<A, B>
|
impl<A, B> MatchName for ProxyObserversTuple<A, B>
|
||||||
|
@ -33,7 +33,7 @@ use crate::{
|
|||||||
executors::{Executor, ExitKind, HasObservers},
|
executors::{Executor, ExitKind, HasObservers},
|
||||||
inputs::{HasTargetBytes, Input},
|
inputs::{HasTargetBytes, Input},
|
||||||
mutators::Tokens,
|
mutators::Tokens,
|
||||||
observers::{get_asan_runtime_flags_with_log_path, ASANBacktraceObserver, ObserversTuple},
|
observers::{get_asan_runtime_flags_with_log_path, AsanBacktraceObserver, ObserversTuple},
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -966,13 +966,13 @@ where
|
|||||||
if self.has_asan_observer.is_none() {
|
if self.has_asan_observer.is_none() {
|
||||||
self.has_asan_observer = Some(
|
self.has_asan_observer = Some(
|
||||||
self.observers()
|
self.observers()
|
||||||
.match_name::<ASANBacktraceObserver>("ASANBacktraceObserver")
|
.match_name::<AsanBacktraceObserver>("AsanBacktraceObserver")
|
||||||
.is_some(),
|
.is_some(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if self.has_asan_observer.unwrap() {
|
if self.has_asan_observer.unwrap() {
|
||||||
self.observers_mut()
|
self.observers_mut()
|
||||||
.match_name_mut::<ASANBacktraceObserver>("ASANBacktraceObserver")
|
.match_name_mut::<AsanBacktraceObserver>("AsanBacktraceObserver")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.parse_asan_output_from_asan_log_file(pid)?;
|
.parse_asan_output_from_asan_log_file(pid)?;
|
||||||
}
|
}
|
||||||
|
@ -85,6 +85,28 @@ pub trait Observer<I, S>: Named + Debug {
|
|||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// If this observer observes `stdout`
|
||||||
|
#[inline]
|
||||||
|
fn observes_stdout(&mut self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
/// If this observer observes `stderr`
|
||||||
|
#[inline]
|
||||||
|
fn observes_stderr(&mut self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
/// React to new `stdout`
|
||||||
|
/// To use this, always return `true` from `observes_stdout`
|
||||||
|
#[inline]
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
fn observe_stdout(&mut self, stdout: &str) {}
|
||||||
|
/// React to new `stderr`
|
||||||
|
/// To use this, always return `true` from `observes_stderr`
|
||||||
|
#[inline]
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
fn observe_stderr(&mut self, stderr: &str) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A haskell-style tuple of observers
|
/// A haskell-style tuple of observers
|
||||||
@ -110,6 +132,16 @@ pub trait ObserversTuple<I, S>: MatchName + Debug {
|
|||||||
input: &I,
|
input: &I,
|
||||||
exit_kind: &ExitKind,
|
exit_kind: &ExitKind,
|
||||||
) -> Result<(), Error>;
|
) -> Result<(), Error>;
|
||||||
|
|
||||||
|
/// Returns true if a `stdout` observer was added to the list
|
||||||
|
fn observes_stdout(&mut self) -> bool;
|
||||||
|
/// Returns true if a `stderr` observer was added to the list
|
||||||
|
fn observes_stderr(&mut self) -> bool;
|
||||||
|
|
||||||
|
/// Runs `observe_stdout` for all stdout observers in the list
|
||||||
|
fn observe_stdout(&mut self, stdout: &str);
|
||||||
|
/// Runs `observe_stderr` for all stderr observers in the list
|
||||||
|
fn observe_stderr(&mut self, stderr: &str);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I, S> ObserversTuple<I, S> for () {
|
impl<I, S> ObserversTuple<I, S> for () {
|
||||||
@ -138,6 +170,26 @@ impl<I, S> ObserversTuple<I, S> for () {
|
|||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if a `stdout` observer was added to the list
|
||||||
|
#[inline]
|
||||||
|
fn observes_stdout(&mut self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
/// Returns true if a `stderr` observer was added to the list
|
||||||
|
#[inline]
|
||||||
|
fn observes_stderr(&mut self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Runs `observe_stdout` for all stdout observers in the list
|
||||||
|
#[inline]
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
fn observe_stdout(&mut self, stdout: &str) {}
|
||||||
|
/// Runs `observe_stderr` for all stderr observers in the list
|
||||||
|
#[inline]
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
fn observe_stderr(&mut self, stderr: &str) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Head, Tail, I, S> ObserversTuple<I, S> for (Head, Tail)
|
impl<Head, Tail, I, S> ObserversTuple<I, S> for (Head, Tail)
|
||||||
@ -174,9 +226,31 @@ where
|
|||||||
self.0.post_exec_child(state, input, exit_kind)?;
|
self.0.post_exec_child(state, input, exit_kind)?;
|
||||||
self.1.post_exec_child_all(state, input, exit_kind)
|
self.1.post_exec_child_all(state, input, exit_kind)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if a `stdout` observer was added to the list
|
||||||
|
#[inline]
|
||||||
|
fn observes_stdout(&mut self) -> bool {
|
||||||
|
self.0.observes_stdout() || self.1.observes_stdout()
|
||||||
|
}
|
||||||
|
/// Returns true if a `stderr` observer was added to the list
|
||||||
|
#[inline]
|
||||||
|
fn observes_stderr(&mut self) -> bool {
|
||||||
|
self.0.observes_stderr() || self.1.observes_stderr()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A trait for obervers with a hash field
|
/// Runs `observe_stdout` for all stdout observers in the list
|
||||||
|
#[inline]
|
||||||
|
fn observe_stdout(&mut self, stdout: &str) {
|
||||||
|
self.0.observe_stdout(stdout);
|
||||||
|
}
|
||||||
|
/// Runs `observe_stderr` for all stderr observers in the list
|
||||||
|
#[inline]
|
||||||
|
fn observe_stderr(&mut self, stderr: &str) {
|
||||||
|
self.1.observe_stderr(stderr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A trait for [`Observer`]`s` with a hash field
|
||||||
pub trait ObserverWithHashField {
|
pub trait ObserverWithHashField {
|
||||||
/// get the value of the hash field
|
/// get the value of the hash field
|
||||||
fn hash(&self) -> &Option<u64>;
|
fn hash(&self) -> &Option<u64>;
|
||||||
@ -876,6 +950,23 @@ pub mod pybind {
|
|||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: expose stdout/stderr to python
|
||||||
|
#[inline]
|
||||||
|
fn observes_stdout(&mut self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn observes_stderr(&mut self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn observe_stderr(&mut self, _: &str) {}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn observe_stdout(&mut self, _: &str) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MatchName for PythonObserversTuple {
|
impl MatchName for PythonObserversTuple {
|
||||||
|
@ -165,12 +165,12 @@ pub fn get_asan_runtime_flags() -> String {
|
|||||||
|
|
||||||
/// An observer looking at the backtrace of target command using ASAN output
|
/// An observer looking at the backtrace of target command using ASAN output
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
pub struct ASANBacktraceObserver {
|
pub struct AsanBacktraceObserver {
|
||||||
observer_name: String,
|
observer_name: String,
|
||||||
hash: Option<u64>,
|
hash: Option<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ASANBacktraceObserver {
|
impl AsanBacktraceObserver {
|
||||||
/// Creates a new [`BacktraceObserver`] with the given name.
|
/// Creates a new [`BacktraceObserver`] with the given name.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(observer_name: &str) -> Self {
|
pub fn new(observer_name: &str) -> Self {
|
||||||
@ -216,7 +216,7 @@ impl ASANBacktraceObserver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObserverWithHashField for ASANBacktraceObserver {
|
impl ObserverWithHashField for AsanBacktraceObserver {
|
||||||
/// Gets the hash value of this observer.
|
/// Gets the hash value of this observer.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn hash(&self) -> &Option<u64> {
|
fn hash(&self) -> &Option<u64> {
|
||||||
@ -234,13 +234,13 @@ impl ObserverWithHashField for ASANBacktraceObserver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ASANBacktraceObserver {
|
impl Default for AsanBacktraceObserver {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new("ASANBacktraceObserver")
|
Self::new("AsanBacktraceObserver")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I, S> Observer<I, S> for ASANBacktraceObserver
|
impl<I, S> Observer<I, S> for AsanBacktraceObserver
|
||||||
where
|
where
|
||||||
I: Debug,
|
I: Debug,
|
||||||
{
|
{
|
||||||
@ -256,9 +256,20 @@ where
|
|||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Do nothing on new `stderr`
|
||||||
|
#[inline]
|
||||||
|
fn observes_stderr(&mut self) -> bool {
|
||||||
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Named for ASANBacktraceObserver {
|
/// Do nothing on new `stderr`
|
||||||
|
fn observe_stderr(&mut self, stderr: &str) {
|
||||||
|
self.parse_asan_output(stderr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Named for AsanBacktraceObserver {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
&self.observer_name
|
&self.observer_name
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
//! The [`StdOutObserver`] and [`StdErrObserver`] observers look at the stdout of a program
|
//! The [`StdOutObserver`] and [`StdErrObserver`] observers look at the stdout of a program
|
||||||
//! The executor must explicitely support these observers.
|
//! The executor must explicitly support these observers.
|
||||||
//! For example, they are supported on the [`crate::executors::CommandExecutor`].
|
//! For example, they are supported on the [`crate::executors::CommandExecutor`].
|
||||||
|
|
||||||
use alloc::string::String;
|
use alloc::string::String;
|
||||||
@ -27,7 +27,16 @@ impl StdOutObserver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I, S> Observer<I, S> for StdOutObserver {}
|
impl<I, S> Observer<I, S> for StdOutObserver {
|
||||||
|
#[inline]
|
||||||
|
fn observes_stdout(&mut self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
/// React to new `stdout`
|
||||||
|
fn observe_stdout(&mut self, stdout: &str) {
|
||||||
|
self.stdout = Some(stdout.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Named for StdOutObserver {
|
impl Named for StdOutObserver {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
@ -54,7 +63,17 @@ impl StdErrObserver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I, S> Observer<I, S> for StdErrObserver {}
|
impl<I, S> Observer<I, S> for StdErrObserver {
|
||||||
|
#[inline]
|
||||||
|
fn observes_stderr(&mut self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Do nothing on new `stderr`
|
||||||
|
fn observe_stderr(&mut self, stderr: &str) {
|
||||||
|
self.stderr = Some(stderr.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Named for StdErrObserver {
|
impl Named for StdErrObserver {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user