Allow two different observers for DiffExecutor (#843)
* DifferentialExecutor for CommandExecutor along with StdIO observer * format * fix CI issues * fix format and unit test * fix documentation * allow three structs and doc only for linux * resolve documentation test failure * minor * running fmt_all.sh * into_executor() takes 4 params, not just 1 Co-authored-by: Dominik Maier <domenukk@gmail.com>
This commit is contained in:
parent
64bc5d5bdb
commit
0307dadcd1
@ -108,7 +108,12 @@ pub fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut executor = MyExecutor { shmem_id }.into_executor(tuple_list!(observer, bt_observer));
|
let mut executor = MyExecutor { shmem_id }.into_executor(
|
||||||
|
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);
|
||||||
|
@ -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))
|
MyCommandConfigurator::default().into_executor(tuple_list!(concolic_observer), None, None, None)
|
||||||
),
|
),
|
||||||
concolic_observer_name,
|
concolic_observer_name,
|
||||||
),
|
),
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
//! The command executor executes a sub program for each run
|
//! The command executor executes a sub program for each run
|
||||||
#[cfg(feature = "std")]
|
use alloc::{string::String, vec::Vec};
|
||||||
use alloc::{borrow::ToOwned, string::String, vec::Vec};
|
|
||||||
use core::{
|
use core::{
|
||||||
fmt::{self, Debug, Formatter},
|
fmt::{self, Debug, Formatter},
|
||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
@ -29,6 +28,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
inputs::HasTargetBytes,
|
inputs::HasTargetBytes,
|
||||||
observers::{ASANBacktraceObserver, ObserversTuple, StdErrObserver, StdOutObserver},
|
observers::{ASANBacktraceObserver, ObserversTuple, StdErrObserver, StdOutObserver},
|
||||||
|
std::borrow::ToOwned,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
use crate::{inputs::Input, Error};
|
use crate::{inputs::Input, Error};
|
||||||
@ -37,19 +37,19 @@ use crate::{inputs::Input, Error};
|
|||||||
/// `StdIn`: The traget reads from stdin
|
/// `StdIn`: The traget reads from stdin
|
||||||
/// `File`: The target reads from the specified [`InputFile`]
|
/// `File`: The target reads from the specified [`InputFile`]
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum InputLocation {
|
enum InputLocation {
|
||||||
/// Mutate a command line argument to deliver an input
|
/// Mutate a commandline argument to deliver an input
|
||||||
Arg {
|
Arg {
|
||||||
/// The offset of the argument to mutate
|
/// The offset of the argument to mutate
|
||||||
argnum: usize,
|
argnum: usize,
|
||||||
},
|
},
|
||||||
/// Deliver input via `StdIn`
|
/// Deliver input via `StdIn`
|
||||||
StdIn,
|
StdIn,
|
||||||
/// Deliver the input via the specified [`InputFile`]
|
/// Deliver the iniput 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 file to write input to. The target should read input from this location.
|
/// The fiel to write input to. The target should read input from this location.
|
||||||
input_file: InputFile,
|
out_file: InputFile,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,11 +71,15 @@ fn clone_command(cmd: &Command) -> Command {
|
|||||||
/// A simple Configurator that takes the most common parameters
|
/// A simple Configurator that takes the most common parameters
|
||||||
/// Writes the input either to stdio or to a file
|
/// Writes the input either to stdio or to a file
|
||||||
/// Use [`CommandExecutor::builder()`] to use this configurator.
|
/// Use [`CommandExecutor::builder()`] to use this configurator.
|
||||||
|
#[allow(clippy::struct_excessive_bools)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct StdCommandConfigurator {
|
pub struct StdCommandConfigurator {
|
||||||
/// If set to true, the child output will remain visible
|
/// If set to true, the child output will remain visible
|
||||||
/// By default, the child output is hidden to increase execution speed
|
/// By default, the child output is hidden to increase execution speed
|
||||||
debug_child: bool,
|
debug_child: bool,
|
||||||
|
has_stdout_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
|
||||||
@ -97,6 +101,13 @@ impl CommandConfigurator for StdCommandConfigurator {
|
|||||||
cmd.stderr(Stdio::null());
|
cmd.stderr(Stdio::null());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.has_stdout_observer {
|
||||||
|
cmd.stdout(Stdio::piped());
|
||||||
|
}
|
||||||
|
if self.has_stderr_observer || self.has_asan_observer {
|
||||||
|
cmd.stderr(Stdio::piped());
|
||||||
|
}
|
||||||
|
|
||||||
for (i, arg) in args.enumerate() {
|
for (i, arg) in args.enumerate() {
|
||||||
if i == *argnum {
|
if i == *argnum {
|
||||||
debug_assert_eq!(arg, "DUMMY");
|
debug_assert_eq!(arg, "DUMMY");
|
||||||
@ -129,8 +140,8 @@ impl CommandConfigurator for StdCommandConfigurator {
|
|||||||
drop(stdin);
|
drop(stdin);
|
||||||
Ok(handle)
|
Ok(handle)
|
||||||
}
|
}
|
||||||
InputLocation::File { input_file } => {
|
InputLocation::File { out_file } => {
|
||||||
input_file.write_buf(input.target_bytes().as_slice())?;
|
out_file.write_buf(input.target_bytes().as_slice())?;
|
||||||
Ok(self.command.spawn()?)
|
Ok(self.command.spawn()?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -149,13 +160,13 @@ where
|
|||||||
configurer: T,
|
configurer: T,
|
||||||
observers: OT,
|
observers: OT,
|
||||||
/// cache if the AsanBacktraceObserver is present
|
/// cache if the AsanBacktraceObserver is present
|
||||||
has_asan_observer: bool,
|
has_asan_observer: Option<String>,
|
||||||
/// If set, we found a [`StdErrObserver`] in the observer list.
|
/// If set, we found a [`StdErrObserver`] in the observer list.
|
||||||
/// Pipe the child's `stderr` instead of closing it.
|
/// Pipe the child's `stderr` instead of closing it.
|
||||||
has_stdout_observer: bool,
|
has_stdout_observer: Option<String>,
|
||||||
/// If set, we found a [`StdOutObserver`] in the observer list
|
/// If set, we found a [`StdOutObserver`] in the observer list
|
||||||
/// Pipe the child's `stdout` instead of closing it.
|
/// Pipe the child's `stdout` instead of closing it.
|
||||||
has_stderr_observer: bool,
|
has_stderr_observer: Option<String>,
|
||||||
phantom: PhantomData<(EM, I, S, Z)>,
|
phantom: PhantomData<(EM, I, S, Z)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -213,6 +224,9 @@ where
|
|||||||
debug_child: bool,
|
debug_child: bool,
|
||||||
observers: OT,
|
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>,
|
||||||
@ -224,35 +238,29 @@ where
|
|||||||
}
|
}
|
||||||
command.stdin(Stdio::null());
|
command.stdin(Stdio::null());
|
||||||
|
|
||||||
let has_stdout_observer = observers
|
if has_stdout_observer.is_some() {
|
||||||
.match_name::<StdOutObserver>("StdOutObserver")
|
command.stdout(Stdio::piped());
|
||||||
.is_some();
|
|
||||||
if has_stdout_observer {
|
|
||||||
command.stderr(Stdio::piped());
|
|
||||||
}
|
}
|
||||||
|
if has_stderr_observer.is_some() || has_asan_observer.is_some() {
|
||||||
let has_stderr_observer = observers
|
|
||||||
.match_name::<StdErrObserver>("StdErrObserver")
|
|
||||||
.is_some();
|
|
||||||
let has_asan_observer = observers
|
|
||||||
.match_name::<ASANBacktraceObserver>("ASANBacktraceObserver")
|
|
||||||
.is_some();
|
|
||||||
if has_stderr_observer || has_asan_observer {
|
|
||||||
command.stderr(Stdio::piped());
|
command.stderr(Stdio::piped());
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
observers,
|
observers,
|
||||||
has_asan_observer,
|
|
||||||
configurer: StdCommandConfigurator {
|
configurer: StdCommandConfigurator {
|
||||||
input_location: InputLocation::File {
|
input_location: InputLocation::File {
|
||||||
input_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,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -264,6 +272,9 @@ where
|
|||||||
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>,
|
||||||
@ -272,6 +283,16 @@ 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() {
|
||||||
@ -340,27 +361,27 @@ where
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if self.has_asan_observer || self.has_stderr_observer {
|
if self.has_asan_observer.is_some() || self.has_stderr_observer.is_some() {
|
||||||
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 self.has_asan_observer {
|
if let Some(name) = self.has_asan_observer.as_ref() {
|
||||||
self.observers
|
self.observers
|
||||||
.match_name_mut::<ASANBacktraceObserver>("ASANBacktraceObserver")
|
.match_name_mut::<ASANBacktraceObserver>(name)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.parse_asan_output(&stderr);
|
.parse_asan_output(&stderr);
|
||||||
}
|
}
|
||||||
if self.has_stderr_observer {
|
if let Some(name) = self.has_stderr_observer.as_ref() {
|
||||||
self.observers
|
self.observers
|
||||||
.match_name_mut::<StdErrObserver>("StdErrObserver")
|
.match_name_mut::<StdErrObserver>(name)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.stderr = Some(stderr);
|
.stderr = Some(stderr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if self.has_stdout_observer {
|
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(
|
||||||
@ -368,7 +389,7 @@ where
|
|||||||
)
|
)
|
||||||
})?.read_to_string(&mut stdout)?;
|
})?.read_to_string(&mut stdout)?;
|
||||||
self.observers
|
self.observers
|
||||||
.match_name_mut::<StdOutObserver>("StdOutObserver")
|
.match_name_mut::<StdOutObserver>(name)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.stdout = Some(stdout);
|
.stdout = Some(stdout);
|
||||||
}
|
}
|
||||||
@ -398,6 +419,9 @@ 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 {
|
||||||
@ -417,9 +441,30 @@ 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
|
||||||
@ -466,9 +511,9 @@ impl CommandExecutorBuilder {
|
|||||||
/// and adds the filename as arg to at the current position.
|
/// and adds the filename as arg to at the current position.
|
||||||
pub fn arg_input_file<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
|
pub fn arg_input_file<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
|
||||||
self.arg(path.as_ref());
|
self.arg(path.as_ref());
|
||||||
let input_file_std = InputFile::create(path.as_ref()).unwrap();
|
let out_file_std = InputFile::create(path.as_ref()).unwrap();
|
||||||
self.input(InputLocation::File {
|
self.input(InputLocation::File {
|
||||||
input_file: input_file_std,
|
out_file: out_file_std,
|
||||||
});
|
});
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -565,29 +610,28 @@ impl CommandExecutorBuilder {
|
|||||||
command.stdout(Stdio::null());
|
command.stdout(Stdio::null());
|
||||||
command.stderr(Stdio::null());
|
command.stderr(Stdio::null());
|
||||||
}
|
}
|
||||||
if observers
|
if self.has_stderr_observer.is_some() || self.has_asan_observer.is_some() {
|
||||||
.match_name::<ASANBacktraceObserver>("ASANBacktraceObserver")
|
|
||||||
.is_some()
|
|
||||||
|| observers
|
|
||||||
.match_name::<StdErrObserver>("StdErrObserver")
|
|
||||||
.is_some()
|
|
||||||
{
|
|
||||||
// we need stderr for ASANBackt
|
// we need stderr for ASANBackt
|
||||||
command.stderr(Stdio::piped());
|
command.stderr(Stdio::piped());
|
||||||
}
|
}
|
||||||
if observers
|
if self.has_stdout_observer.is_some() {
|
||||||
.match_name::<StdOutObserver>("StdOutObserver")
|
|
||||||
.is_some()
|
|
||||||
{
|
|
||||||
command.stdout(Stdio::piped());
|
command.stdout(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_stderr_observer: self.has_stderr_observer.is_some(),
|
||||||
|
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(observers))
|
Ok(configurator.into_executor::<EM, I, OT, S, Z>(
|
||||||
|
observers,
|
||||||
|
self.has_stdout_observer.clone(),
|
||||||
|
self.has_stderr_observer.clone(),
|
||||||
|
self.has_asan_observer.clone(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -619,9 +663,10 @@ 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(())
|
/// MyExecutor.into_executor((), None, None, None)
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
#[cfg(all(feature = "std", any(unix, doc)))]
|
#[cfg(all(feature = "std", any(unix, doc)))]
|
||||||
pub trait CommandConfigurator: Sized + Debug {
|
pub trait CommandConfigurator: Sized + Debug {
|
||||||
/// Spawns a new process with the given configuration.
|
/// Spawns a new process with the given configuration.
|
||||||
@ -630,22 +675,16 @@ 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>(self, observers: OT) -> CommandExecutor<EM, I, OT, S, Self, Z>
|
fn into_executor<EM, I, OT, S, Z>(
|
||||||
|
self,
|
||||||
|
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,
|
||||||
{
|
{
|
||||||
let has_asan_observer = observers
|
|
||||||
.match_name::<ASANBacktraceObserver>("ASANBacktraceObserver")
|
|
||||||
.is_some();
|
|
||||||
|
|
||||||
let has_stdout_observer = observers
|
|
||||||
.match_name::<StdOutObserver>("StdOutObserver")
|
|
||||||
.is_some();
|
|
||||||
|
|
||||||
let has_stderr_observer = observers
|
|
||||||
.match_name::<StdErrObserver>("StdErrObserver")
|
|
||||||
.is_some();
|
|
||||||
|
|
||||||
CommandExecutor {
|
CommandExecutor {
|
||||||
observers,
|
observers,
|
||||||
has_asan_observer,
|
has_asan_observer,
|
||||||
@ -697,13 +736,18 @@ mod tests {
|
|||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
fn test_parse_afl_cmdline() {
|
fn test_parse_afl_cmdline() {
|
||||||
use alloc::string::ToString;
|
use alloc::string::ToString;
|
||||||
|
|
||||||
let mut mgr = SimpleEventManager::<BytesInput, _, ()>::new(SimpleMonitor::new(|status| {
|
let mut mgr = SimpleEventManager::<BytesInput, _, ()>::new(SimpleMonitor::new(|status| {
|
||||||
println!("{status}");
|
println!("{status}");
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let mut executor =
|
let mut executor = CommandExecutor::parse_afl_cmdline(
|
||||||
CommandExecutor::parse_afl_cmdline(&["file".to_string(), "@@".to_string()], (), true)
|
&["file".to_string(), "@@".to_string()],
|
||||||
|
(),
|
||||||
|
true,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
executor
|
executor
|
||||||
.run_target(
|
.run_target(
|
||||||
|
@ -2,9 +2,12 @@
|
|||||||
//! It wraps two exeutors that will be run after each other with the same input.
|
//! It wraps two exeutors that will be run after each other with the same input.
|
||||||
//! In comparison to the [`crate::executors::CombinedExecutor`] it also runs the secondary executor in `run_target`.
|
//! In comparison to the [`crate::executors::CombinedExecutor`] it also runs the secondary executor in `run_target`.
|
||||||
//!
|
//!
|
||||||
use core::fmt::Debug;
|
use core::{cell::UnsafeCell, fmt::Debug};
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
bolts::{ownedref::OwnedPtrMut, tuples::MatchName},
|
||||||
executors::{Executor, ExitKind, HasObservers},
|
executors::{Executor, ExitKind, HasObservers},
|
||||||
inputs::Input,
|
inputs::Input,
|
||||||
observers::ObserversTuple,
|
observers::ObserversTuple,
|
||||||
@ -13,19 +16,24 @@ use crate::{
|
|||||||
|
|
||||||
/// A [`DiffExecutor`] wraps a primary executor, forwarding its methods, and a secondary one
|
/// A [`DiffExecutor`] wraps a primary executor, forwarding its methods, and a secondary one
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct DiffExecutor<A, B>
|
pub struct DiffExecutor<A, B, OTA, OTB>
|
||||||
where
|
where
|
||||||
A: Debug,
|
A: Debug,
|
||||||
B: Debug,
|
B: Debug,
|
||||||
|
OTA: Debug,
|
||||||
|
OTB: Debug,
|
||||||
{
|
{
|
||||||
primary: A,
|
primary: A,
|
||||||
secondary: B,
|
secondary: B,
|
||||||
|
observers: UnsafeCell<ProxyObserversTuple<OTA, OTB>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A, B> DiffExecutor<A, B>
|
impl<A, B, OTA, OTB> DiffExecutor<A, B, OTA, OTB>
|
||||||
where
|
where
|
||||||
A: Debug,
|
A: Debug,
|
||||||
B: Debug,
|
B: Debug,
|
||||||
|
OTA: Debug,
|
||||||
|
OTB: Debug,
|
||||||
{
|
{
|
||||||
/// Create a new `DiffExecutor`, wrapping the given `executor`s.
|
/// Create a new `DiffExecutor`, wrapping the given `executor`s.
|
||||||
pub fn new<EM, I, S, Z>(primary: A, secondary: B) -> Self
|
pub fn new<EM, I, S, Z>(primary: A, secondary: B) -> Self
|
||||||
@ -34,7 +42,14 @@ where
|
|||||||
B: Executor<EM, I, S, Z>,
|
B: Executor<EM, I, S, Z>,
|
||||||
I: Input,
|
I: Input,
|
||||||
{
|
{
|
||||||
Self { primary, secondary }
|
Self {
|
||||||
|
primary,
|
||||||
|
secondary,
|
||||||
|
observers: UnsafeCell::new(ProxyObserversTuple {
|
||||||
|
primary: OwnedPtrMut::Ptr(core::ptr::null_mut()),
|
||||||
|
secondary: OwnedPtrMut::Ptr(core::ptr::null_mut()),
|
||||||
|
}),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieve the primary `Executor` that is wrapped by this `DiffExecutor`.
|
/// Retrieve the primary `Executor` that is wrapped by this `DiffExecutor`.
|
||||||
@ -48,11 +63,13 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A, B, EM, I, S, Z> Executor<EM, I, S, Z> for DiffExecutor<A, B>
|
impl<A, B, EM, I, OTA, OTB, S, Z> Executor<EM, I, S, Z> for DiffExecutor<A, B, OTA, OTB>
|
||||||
where
|
where
|
||||||
A: Executor<EM, I, S, Z>,
|
A: Executor<EM, I, S, Z>,
|
||||||
B: Executor<EM, I, S, Z>,
|
B: Executor<EM, I, S, Z>,
|
||||||
I: Input,
|
I: Input,
|
||||||
|
OTA: Debug,
|
||||||
|
OTB: Debug,
|
||||||
{
|
{
|
||||||
fn run_target(
|
fn run_target(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -77,19 +94,115 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A, B, I, OT, S> HasObservers<I, OT, S> for DiffExecutor<A, B>
|
/// Proxy the observers of the inner executors
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
#[serde(
|
||||||
|
bound = "A: serde::Serialize + serde::de::DeserializeOwned, B: serde::Serialize + serde::de::DeserializeOwned"
|
||||||
|
)]
|
||||||
|
pub struct ProxyObserversTuple<A, B> {
|
||||||
|
primary: OwnedPtrMut<A>,
|
||||||
|
secondary: OwnedPtrMut<B>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<A, B, I, S> ObserversTuple<I, S> for ProxyObserversTuple<A, B>
|
||||||
where
|
where
|
||||||
A: HasObservers<I, OT, S>,
|
A: ObserversTuple<I, S>,
|
||||||
B: Debug,
|
B: ObserversTuple<I, S>,
|
||||||
OT: ObserversTuple<I, S>,
|
{
|
||||||
|
fn pre_exec_all(&mut self, state: &mut S, input: &I) -> Result<(), Error> {
|
||||||
|
self.primary.as_mut().pre_exec_all(state, input)?;
|
||||||
|
self.secondary.as_mut().pre_exec_all(state, input)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn post_exec_all(
|
||||||
|
&mut self,
|
||||||
|
state: &mut S,
|
||||||
|
input: &I,
|
||||||
|
exit_kind: &ExitKind,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
self.primary
|
||||||
|
.as_mut()
|
||||||
|
.post_exec_all(state, input, exit_kind)?;
|
||||||
|
self.secondary
|
||||||
|
.as_mut()
|
||||||
|
.post_exec_all(state, input, exit_kind)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pre_exec_child_all(&mut self, state: &mut S, input: &I) -> Result<(), Error> {
|
||||||
|
self.primary.as_mut().pre_exec_child_all(state, input)?;
|
||||||
|
self.secondary.as_mut().pre_exec_child_all(state, input)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn post_exec_child_all(
|
||||||
|
&mut self,
|
||||||
|
state: &mut S,
|
||||||
|
input: &I,
|
||||||
|
exit_kind: &ExitKind,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
self.primary
|
||||||
|
.as_mut()
|
||||||
|
.post_exec_child_all(state, input, exit_kind)?;
|
||||||
|
self.secondary
|
||||||
|
.as_mut()
|
||||||
|
.post_exec_child_all(state, input, exit_kind)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<A, B> MatchName for ProxyObserversTuple<A, B>
|
||||||
|
where
|
||||||
|
A: MatchName,
|
||||||
|
B: MatchName,
|
||||||
|
{
|
||||||
|
fn match_name<T>(&self, name: &str) -> Option<&T> {
|
||||||
|
if let Some(t) = self.primary.as_ref().match_name::<T>(name) {
|
||||||
|
return Some(t);
|
||||||
|
}
|
||||||
|
self.secondary.as_ref().match_name::<T>(name)
|
||||||
|
}
|
||||||
|
fn match_name_mut<T>(&mut self, name: &str) -> Option<&mut T> {
|
||||||
|
if let Some(t) = self.primary.as_mut().match_name_mut::<T>(name) {
|
||||||
|
return Some(t);
|
||||||
|
}
|
||||||
|
self.secondary.as_mut().match_name_mut::<T>(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<A, B> ProxyObserversTuple<A, B> {
|
||||||
|
fn set(&mut self, primary: &A, secondary: &B) {
|
||||||
|
self.primary = OwnedPtrMut::Ptr(primary as *const A as *mut A);
|
||||||
|
self.secondary = OwnedPtrMut::Ptr(secondary as *const B as *mut B);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<A, B, I, OTA, OTB, S> HasObservers<I, ProxyObserversTuple<OTA, OTB>, S>
|
||||||
|
for DiffExecutor<A, B, OTA, OTB>
|
||||||
|
where
|
||||||
|
A: HasObservers<I, OTA, S>,
|
||||||
|
B: HasObservers<I, OTB, S>,
|
||||||
|
OTA: ObserversTuple<I, S>,
|
||||||
|
OTB: ObserversTuple<I, S>,
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn observers(&self) -> &OT {
|
fn observers(&self) -> &ProxyObserversTuple<OTA, OTB> {
|
||||||
self.primary.observers()
|
unsafe {
|
||||||
|
self.observers
|
||||||
|
.get()
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.set(self.primary.observers(), self.secondary.observers());
|
||||||
|
self.observers.get().as_ref().unwrap()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn observers_mut(&mut self) -> &mut OT {
|
fn observers_mut(&mut self) -> &mut ProxyObserversTuple<OTA, OTB> {
|
||||||
self.primary.observers_mut()
|
unsafe {
|
||||||
|
self.observers
|
||||||
|
.get()
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.set(self.primary.observers(), self.secondary.observers());
|
||||||
|
self.observers.get().as_mut().unwrap()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -140,7 +140,6 @@ where
|
|||||||
fn err(name: &str) -> Error {
|
fn err(name: &str) -> Error {
|
||||||
Error::illegal_argument(format!("DiffFeedback: observer {name} not found"))
|
Error::illegal_argument(format!("DiffFeedback: observer {name} not found"))
|
||||||
}
|
}
|
||||||
|
|
||||||
let o1: &O1 = observers
|
let o1: &O1 = observers
|
||||||
.match_name(&self.o1_name)
|
.match_name(&self.o1_name)
|
||||||
.ok_or_else(|| err(&self.o1_name))?;
|
.ok_or_else(|| err(&self.o1_name))?;
|
||||||
@ -148,7 +147,7 @@ where
|
|||||||
.match_name(&self.o2_name)
|
.match_name(&self.o2_name)
|
||||||
.ok_or_else(|| err(&self.o2_name))?;
|
.ok_or_else(|| err(&self.o2_name))?;
|
||||||
|
|
||||||
Ok(o1 != o2)
|
Ok((self.compare_fn)(o1, o2) == DiffResult::Diff)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,11 +4,13 @@
|
|||||||
|
|
||||||
use alloc::string::String;
|
use alloc::string::String;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{bolts::tuples::Named, observers::Observer};
|
use crate::{bolts::tuples::Named, observers::Observer};
|
||||||
|
|
||||||
/// An observer that captures stdout of a target.
|
/// An observer that captures stdout of a target.
|
||||||
/// Only works for supported executors.
|
/// Only works for supported executors.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
pub struct StdOutObserver {
|
pub struct StdOutObserver {
|
||||||
/// The name of the observer.
|
/// The name of the observer.
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user