Introduce HasAflStyleTargetArguments and restore parse_afl_cmdline for CommandExecutor (#3125)
* Introduce HasAflStyleTargetArguments and restore parse_afl_cmdline for CommandExecutor * clippy * Fix imports * Move envs to the shared trait * Move to a standalone file * Format * Gate via std and unix * Fix * clippy * move InputLocation to afl_args * clippy * Fix * fmt * Fix more * Shall last missing * Rename to AflTargetArgs * Fmt * move AflTargetArgs to libafl_bolts and adapt CMainArgsBuilder to use it * use_stdin is no longer needed * leaveout * Fix again * Renaming * Leave out lib.rs * Leave out fmt --------- Co-authored-by: Dongjia "toka" Zhang <tokazerkje@outlook.com>
This commit is contained in:
parent
a7d735c1de
commit
fa8a576ef0
@ -25,7 +25,7 @@ use libafl_bolts::{
|
||||
rands::StdRand,
|
||||
shmem::{ShMem, ShMemProvider},
|
||||
tuples::tuple_list,
|
||||
AsSliceMut,
|
||||
AsSliceMut, TargetArgs,
|
||||
};
|
||||
|
||||
pub fn main() {
|
||||
|
@ -22,7 +22,7 @@ use libafl_bolts::{
|
||||
rands::StdRand,
|
||||
shmem::{ShMem, ShMemProvider, UnixShMemProvider},
|
||||
tuples::{tuple_list, Handled, Merge},
|
||||
AsSliceMut, Truncate,
|
||||
AsSliceMut, TargetArgs, Truncate,
|
||||
};
|
||||
use libafl_targets::EDGES_MAP_DEFAULT_SIZE;
|
||||
use nix::sys::signal::Signal;
|
||||
|
@ -19,7 +19,7 @@ use libafl::{
|
||||
state::{HasCorpus, StdState},
|
||||
};
|
||||
use libafl_bolts::{
|
||||
AsSliceMut, Truncate, current_nanos,
|
||||
AsSliceMut, TargetArgs, Truncate, current_nanos,
|
||||
rands::StdRand,
|
||||
shmem::{ShMem, ShMemProvider, UnixShMemProvider},
|
||||
tuples::{Handled, Merge, tuple_list},
|
||||
|
@ -38,7 +38,7 @@ use libafl_bolts::{
|
||||
rands::StdRand,
|
||||
shmem::{ShMem, ShMemProvider, UnixShMemProvider},
|
||||
tuples::{tuple_list, Merge},
|
||||
AsSliceMut,
|
||||
AsSliceMut, TargetArgs,
|
||||
};
|
||||
use libafl_targets::cmps::AFLppCmpLogMap;
|
||||
use nix::sys::signal::Signal;
|
||||
|
@ -37,7 +37,7 @@ use libafl_bolts::{
|
||||
rands::StdRand,
|
||||
shmem::{ShMem, ShMemProvider, UnixShMemProvider},
|
||||
tuples::{tuple_list, Handled, Merge},
|
||||
AsSliceMut,
|
||||
AsSliceMut, TargetArgs,
|
||||
};
|
||||
use libafl_targets::{
|
||||
cmps::{observers::AFLppCmpLogObserver, stages::AFLppCmplogTracingStage},
|
||||
|
@ -38,7 +38,7 @@ use libafl_bolts::{
|
||||
rands::StdRand,
|
||||
shmem::{ShMem, ShMemProvider, UnixShMemProvider},
|
||||
tuples::{tuple_list, Handled, Merge},
|
||||
AsSliceMut,
|
||||
AsSliceMut, TargetArgs,
|
||||
};
|
||||
use libafl_targets::cmps::AFLppCmpLogMap;
|
||||
use nix::sys::signal::Signal;
|
||||
|
@ -51,7 +51,7 @@ use libafl_bolts::{
|
||||
rands::StdRand,
|
||||
shmem::{ShMem, ShMemProvider, UnixShMemProvider},
|
||||
tuples::{tuple_list, Handled, Merge},
|
||||
AsSliceMut,
|
||||
AsSliceMut, TargetArgs,
|
||||
};
|
||||
#[cfg(feature = "nyx")]
|
||||
use libafl_nyx::{executor::NyxExecutor, helper::NyxHelper, settings::NyxSettings};
|
||||
|
@ -29,7 +29,7 @@ use libafl_bolts::{
|
||||
rands::StdRand,
|
||||
shmem::{ShMem, ShMemProvider, UnixShMemProvider},
|
||||
tuples::{tuple_list, Handled},
|
||||
AsSliceMut, Truncate,
|
||||
AsSliceMut, TargetArgs, Truncate,
|
||||
};
|
||||
use nix::sys::signal::Signal;
|
||||
|
||||
|
@ -23,8 +23,7 @@ use std::{
|
||||
#[cfg(all(feature = "intel_pt", target_os = "linux"))]
|
||||
use libafl_bolts::core_affinity::CoreId;
|
||||
use libafl_bolts::{
|
||||
AsSlice,
|
||||
fs::{InputFile, get_unique_std_input_file},
|
||||
AsSlice, InputLocation, TargetArgs,
|
||||
tuples::{Handle, MatchName, RefIndexable},
|
||||
};
|
||||
#[cfg(all(feature = "intel_pt", target_os = "linux"))]
|
||||
@ -59,27 +58,6 @@ use crate::{
|
||||
std::borrow::ToOwned,
|
||||
};
|
||||
|
||||
/// How to deliver input to an external program
|
||||
/// `StdIn`: The target reads from stdin
|
||||
/// `File`: The target reads from the specified [`InputFile`]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
||||
pub enum InputLocation {
|
||||
/// Mutate a commandline argument to deliver an input
|
||||
Arg {
|
||||
/// The offset of the argument to mutate
|
||||
argnum: usize,
|
||||
},
|
||||
/// Deliver input via `StdIn`
|
||||
#[default]
|
||||
StdIn,
|
||||
/// Deliver the input via the specified [`InputFile`]
|
||||
/// You can use specify [`InputFile::create(INPUTFILE_STD)`] to use a default filename.
|
||||
File {
|
||||
/// The file to write input to. The target should read input from this location.
|
||||
out_file: InputFile,
|
||||
},
|
||||
}
|
||||
|
||||
/// A simple Configurator that takes the most common parameters
|
||||
/// Writes the input either to stdio or to a file
|
||||
/// Use [`CommandExecutor::builder()`] to use this configurator.
|
||||
@ -530,6 +508,40 @@ pub struct CommandExecutorBuilder {
|
||||
timeout: Duration,
|
||||
}
|
||||
|
||||
impl TargetArgs for CommandExecutorBuilder {
|
||||
fn arguments_ref(&self) -> &Vec<OsString> {
|
||||
&self.args
|
||||
}
|
||||
|
||||
fn arguments_mut(&mut self) -> &mut Vec<OsString> {
|
||||
&mut self.args
|
||||
}
|
||||
|
||||
fn envs_ref(&self) -> &Vec<(OsString, OsString)> {
|
||||
&self.envs
|
||||
}
|
||||
|
||||
fn envs_mut(&mut self) -> &mut Vec<(OsString, OsString)> {
|
||||
&mut self.envs
|
||||
}
|
||||
|
||||
fn program_ref(&self) -> &Option<OsString> {
|
||||
&self.program
|
||||
}
|
||||
|
||||
fn program_mut(&mut self) -> &mut Option<OsString> {
|
||||
&mut self.program
|
||||
}
|
||||
|
||||
fn input_location_ref(&self) -> &InputLocation {
|
||||
&self.input_location
|
||||
}
|
||||
|
||||
fn input_location_mut(&mut self) -> &mut InputLocation {
|
||||
&mut self.input_location
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for CommandExecutorBuilder {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
@ -553,40 +565,6 @@ impl CommandExecutorBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the binary to execute
|
||||
/// This option is required.
|
||||
pub fn program<O>(&mut self, program: O) -> &mut Self
|
||||
where
|
||||
O: AsRef<OsStr>,
|
||||
{
|
||||
self.program = Some(program.as_ref().to_owned());
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the input mode and location.
|
||||
/// This option is mandatory, if not set, the `build` method will error.
|
||||
fn input(&mut self, input: InputLocation) -> &mut Self {
|
||||
// This is a fatal error in the user code, no point in returning Err.
|
||||
assert_eq!(
|
||||
self.input_location,
|
||||
InputLocation::StdIn,
|
||||
"input location already set to non-stdin, cannot set it again"
|
||||
);
|
||||
self.input_location = input;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the input mode to [`InputLocation::Arg`] and uses the current arg offset as `argnum`.
|
||||
/// During execution, at input will be provided _as argument_ at this position.
|
||||
/// Use [`Self::arg_input_file_std`] if you want to provide the input as a file instead.
|
||||
pub fn arg_input_arg(&mut self) -> &mut Self {
|
||||
let argnum = self.args.len();
|
||||
self.input(InputLocation::Arg { argnum });
|
||||
// Placeholder arg that gets replaced with the input name later.
|
||||
self.arg("PLACEHOLDER");
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the stdout observer
|
||||
pub fn stdout_observer(&mut self, stdout: Handle<StdOutObserver>) -> &mut Self {
|
||||
self.stdout = Some(stdout);
|
||||
@ -599,68 +577,6 @@ impl CommandExecutorBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the input mode to [`InputLocation::File`]
|
||||
/// and adds the filename as arg to at the current position.
|
||||
/// Uses a default filename.
|
||||
/// Use [`Self::arg_input_file`] to specify a custom filename.
|
||||
pub fn arg_input_file_std(&mut self) -> &mut Self {
|
||||
self.arg_input_file(get_unique_std_input_file());
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the input mode to [`InputLocation::File`]
|
||||
/// 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 {
|
||||
self.arg(path.as_ref());
|
||||
let out_file_std = InputFile::create(path.as_ref()).unwrap();
|
||||
self.input(InputLocation::File {
|
||||
out_file: out_file_std,
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds an argument to the program's commandline.
|
||||
pub fn arg<O: AsRef<OsStr>>(&mut self, arg: O) -> &mut CommandExecutorBuilder {
|
||||
self.args.push(arg.as_ref().to_owned());
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a range of arguments to the program's commandline.
|
||||
pub fn args<IT, O>(&mut self, args: IT) -> &mut CommandExecutorBuilder
|
||||
where
|
||||
IT: IntoIterator<Item = O>,
|
||||
O: AsRef<OsStr>,
|
||||
{
|
||||
for arg in args {
|
||||
self.arg(arg.as_ref());
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a range of environment variables to the executed command.
|
||||
pub fn envs<IT, K, V>(&mut self, vars: IT) -> &mut CommandExecutorBuilder
|
||||
where
|
||||
IT: IntoIterator<Item = (K, V)>,
|
||||
K: AsRef<OsStr>,
|
||||
V: AsRef<OsStr>,
|
||||
{
|
||||
for (ref key, ref val) in vars {
|
||||
self.env(key.as_ref(), val.as_ref());
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds an environment variable to the executed command.
|
||||
pub fn env<K, V>(&mut self, key: K, val: V) -> &mut CommandExecutorBuilder
|
||||
where
|
||||
K: AsRef<OsStr>,
|
||||
V: AsRef<OsStr>,
|
||||
{
|
||||
self.envs
|
||||
.push((key.as_ref().to_owned(), val.as_ref().to_owned()));
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the working directory for the child process.
|
||||
pub fn current_dir<P: AsRef<Path>>(&mut self, dir: P) -> &mut CommandExecutorBuilder {
|
||||
self.cwd = Some(dir.as_ref().to_owned());
|
||||
@ -865,6 +781,8 @@ fn waitpid_filtered(pid: Pid, options: Option<WaitPidFlag>) -> Result<WaitStatus
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use libafl_bolts::TargetArgs;
|
||||
|
||||
use crate::{
|
||||
events::SimpleEventManager,
|
||||
executors::{
|
||||
@ -885,8 +803,7 @@ mod tests {
|
||||
log::info!("{status}");
|
||||
}));
|
||||
|
||||
let mut executor = CommandExecutor::builder();
|
||||
executor
|
||||
let executor = CommandExecutor::builder()
|
||||
.program("ls")
|
||||
.input(InputLocation::Arg { argnum: 0 });
|
||||
let executor = executor.build(());
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! Expose an `Executor` based on a `Forkserver` in order to execute AFL/AFL++ binaries
|
||||
|
||||
use alloc::{borrow::ToOwned, string::ToString, vec::Vec};
|
||||
use alloc::{string::ToString, vec::Vec};
|
||||
use core::{
|
||||
fmt::{self, Debug, Formatter},
|
||||
marker::PhantomData,
|
||||
@ -8,18 +8,17 @@ use core::{
|
||||
};
|
||||
use std::{
|
||||
env,
|
||||
ffi::{OsStr, OsString},
|
||||
ffi::OsString,
|
||||
io::{self, ErrorKind, Read, Write},
|
||||
os::{
|
||||
fd::{AsRawFd, BorrowedFd},
|
||||
unix::{io::RawFd, process::CommandExt},
|
||||
},
|
||||
path::Path,
|
||||
process::{Child, Command, Stdio},
|
||||
};
|
||||
|
||||
use libafl_bolts::{
|
||||
AsSlice, AsSliceMut, Truncate,
|
||||
AsSlice, AsSliceMut, InputLocation, TargetArgs, Truncate,
|
||||
fs::{InputFile, get_unique_std_input_file},
|
||||
os::{dup2, pipes::Pipe},
|
||||
ownedref::OwnedSlice,
|
||||
@ -777,12 +776,11 @@ pub struct ForkserverExecutorBuilder<'a, TC, SP> {
|
||||
arguments: Vec<OsString>,
|
||||
envs: Vec<(OsString, OsString)>,
|
||||
debug_child: bool,
|
||||
use_stdin: bool,
|
||||
uses_shmem_testcase: bool,
|
||||
is_persistent: bool,
|
||||
is_deferred_frksrv: bool,
|
||||
autotokens: Option<&'a mut Tokens>,
|
||||
input_filename: Option<OsString>,
|
||||
input_location: InputLocation,
|
||||
shmem_provider: Option<&'a mut SP>,
|
||||
max_input_size: usize,
|
||||
min_input_size: usize,
|
||||
@ -795,6 +793,43 @@ pub struct ForkserverExecutorBuilder<'a, TC, SP> {
|
||||
target_bytes_converter: TC,
|
||||
}
|
||||
|
||||
impl<TC, SP> TargetArgs for ForkserverExecutorBuilder<'_, TC, SP> {
|
||||
fn arguments_ref(&self) -> &Vec<OsString> {
|
||||
&self.arguments
|
||||
}
|
||||
fn arguments_mut(&mut self) -> &mut Vec<OsString> {
|
||||
&mut self.arguments
|
||||
}
|
||||
|
||||
fn envs_ref(&self) -> &Vec<(OsString, OsString)> {
|
||||
&self.envs
|
||||
}
|
||||
|
||||
fn envs_mut(&mut self) -> &mut Vec<(OsString, OsString)> {
|
||||
&mut self.envs
|
||||
}
|
||||
|
||||
fn program_ref(&self) -> &Option<OsString> {
|
||||
&self.program
|
||||
}
|
||||
|
||||
fn program_mut(&mut self) -> &mut Option<OsString> {
|
||||
&mut self.program
|
||||
}
|
||||
|
||||
fn input_location_ref(&self) -> &InputLocation {
|
||||
&self.input_location
|
||||
}
|
||||
|
||||
fn input_location_mut(&mut self) -> &mut InputLocation {
|
||||
&mut self.input_location
|
||||
}
|
||||
|
||||
fn arg_input_arg(self) -> Self {
|
||||
panic!("ForkserverExecutor doesn't support mutating arguments")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, TC, SHM, SP> ForkserverExecutorBuilder<'a, TC, SP>
|
||||
where
|
||||
SHM: ShMem,
|
||||
@ -821,7 +856,7 @@ where
|
||||
"ForkserverExecutor: program: {:?}, arguments: {:?}, use_stdin: {:?}",
|
||||
target,
|
||||
self.arguments.clone(),
|
||||
self.use_stdin
|
||||
self.use_stdin()
|
||||
);
|
||||
|
||||
if self.uses_shmem_testcase && map.is_none() {
|
||||
@ -887,7 +922,7 @@ where
|
||||
"ForkserverExecutor: program: {:?}, arguments: {:?}, use_stdin: {:?}, map_size: {:?}",
|
||||
target,
|
||||
self.arguments.clone(),
|
||||
self.use_stdin,
|
||||
self.use_stdin(),
|
||||
self.map_size
|
||||
);
|
||||
|
||||
@ -933,16 +968,16 @@ where
|
||||
|
||||
#[expect(clippy::pedantic)]
|
||||
fn build_helper(&mut self) -> Result<(Forkserver, InputFile, Option<SHM>), Error> {
|
||||
let input_filename = match &self.input_filename {
|
||||
Some(name) => name.clone(),
|
||||
None => {
|
||||
self.use_stdin = true;
|
||||
OsString::from(get_unique_std_input_file())
|
||||
let input_file = match &self.input_location {
|
||||
InputLocation::StdIn => InputFile::create(OsString::from(get_unique_std_input_file()))?,
|
||||
InputLocation::Arg { argnum: _ } => {
|
||||
return Err(Error::illegal_argument(
|
||||
"forkserver doesn't support argument mutation",
|
||||
));
|
||||
}
|
||||
InputLocation::File { out_file } => out_file.clone(),
|
||||
};
|
||||
|
||||
let input_file = InputFile::create(input_filename)?;
|
||||
|
||||
let map = match &mut self.shmem_provider {
|
||||
None => None,
|
||||
Some(provider) => {
|
||||
@ -966,7 +1001,7 @@ where
|
||||
self.arguments.clone(),
|
||||
self.envs.clone(),
|
||||
input_file.as_raw_fd(),
|
||||
self.use_stdin,
|
||||
self.use_stdin(),
|
||||
0,
|
||||
self.is_persistent,
|
||||
self.is_deferred_frksrv,
|
||||
@ -1226,94 +1261,6 @@ where
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
/// Parse afl style command line
|
||||
///
|
||||
/// Replaces `@@` with the path to the input file generated by the fuzzer. If `@@` is omitted,
|
||||
/// `stdin` is used to pass the test case instead.
|
||||
///
|
||||
/// Interprets the first argument as the path to the program as long as it is not set yet.
|
||||
/// You have to omit the program path in case you have set it already. Otherwise
|
||||
/// it will be interpreted as a regular argument, leading to probably unintended results.
|
||||
pub fn parse_afl_cmdline<IT, O>(self, args: IT) -> Self
|
||||
where
|
||||
IT: IntoIterator<Item = O>,
|
||||
O: AsRef<OsStr>,
|
||||
{
|
||||
let mut moved = self;
|
||||
|
||||
let mut use_arg_0_as_program = false;
|
||||
if moved.program.is_none() {
|
||||
use_arg_0_as_program = true;
|
||||
}
|
||||
|
||||
for item in args {
|
||||
if use_arg_0_as_program {
|
||||
moved = moved.program(item);
|
||||
// After the program has been set, unset `use_arg_0_as_program` to treat all
|
||||
// subsequent arguments as regular arguments
|
||||
use_arg_0_as_program = false;
|
||||
} else if item.as_ref() == "@@" {
|
||||
match &moved.input_filename.clone() {
|
||||
Some(name) => {
|
||||
// If the input file name has been modified, use this one
|
||||
moved = moved.arg_input_file(name);
|
||||
}
|
||||
_ => {
|
||||
moved = moved.arg_input_file_std();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
moved = moved.arg(item);
|
||||
}
|
||||
}
|
||||
|
||||
// If we have not set an input file, use stdin as it is AFLs default
|
||||
moved.use_stdin = moved.input_filename.is_none();
|
||||
moved
|
||||
}
|
||||
|
||||
/// The harness
|
||||
#[must_use]
|
||||
pub fn program<O>(mut self, program: O) -> Self
|
||||
where
|
||||
O: AsRef<OsStr>,
|
||||
{
|
||||
self.program = Some(program.as_ref().to_owned());
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds an argument to the harness's commandline
|
||||
///
|
||||
/// You may want to use `parse_afl_cmdline` if you're going to pass `@@`
|
||||
/// represents the input file generated by the fuzzer (similar to the `afl-fuzz` command line).
|
||||
#[must_use]
|
||||
pub fn arg<O>(mut self, arg: O) -> Self
|
||||
where
|
||||
O: AsRef<OsStr>,
|
||||
{
|
||||
self.arguments.push(arg.as_ref().to_owned());
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds arguments to the harness's commandline
|
||||
///
|
||||
/// You may want to use `parse_afl_cmdline` if you're going to pass `@@`
|
||||
/// represents the input file generated by the fuzzer (similar to the `afl-fuzz` command line).
|
||||
#[must_use]
|
||||
pub fn args<IT, O>(mut self, args: IT) -> Self
|
||||
where
|
||||
IT: IntoIterator<Item = O>,
|
||||
O: AsRef<OsStr>,
|
||||
{
|
||||
let mut res = vec![];
|
||||
for arg in args {
|
||||
res.push(arg.as_ref().to_owned());
|
||||
}
|
||||
self.arguments.append(&mut res);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the max input size
|
||||
#[must_use]
|
||||
pub fn max_input_size(mut self, size: usize) -> Self {
|
||||
@ -1328,61 +1275,6 @@ where
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds an environmental var to the harness's commandline
|
||||
#[must_use]
|
||||
pub fn env<K, V>(mut self, key: K, val: V) -> Self
|
||||
where
|
||||
K: AsRef<OsStr>,
|
||||
V: AsRef<OsStr>,
|
||||
{
|
||||
self.envs
|
||||
.push((key.as_ref().to_owned(), val.as_ref().to_owned()));
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds environmental vars to the harness's commandline
|
||||
#[must_use]
|
||||
pub fn envs<IT, K, V>(mut self, vars: IT) -> Self
|
||||
where
|
||||
IT: IntoIterator<Item = (K, V)>,
|
||||
K: AsRef<OsStr>,
|
||||
V: AsRef<OsStr>,
|
||||
{
|
||||
let mut res = vec![];
|
||||
for (ref key, ref val) in vars {
|
||||
res.push((key.as_ref().to_owned(), val.as_ref().to_owned()));
|
||||
}
|
||||
self.envs.append(&mut res);
|
||||
self
|
||||
}
|
||||
|
||||
/// Place the input at this position and set the filename for the input.
|
||||
///
|
||||
/// Note: If you use this, you should ensure that there is only one instance using this
|
||||
/// file at any given time.
|
||||
#[must_use]
|
||||
pub fn arg_input_file<P: AsRef<Path>>(self, path: P) -> Self {
|
||||
let mut moved = self.arg(path.as_ref());
|
||||
|
||||
let path_as_string = path.as_ref().as_os_str().to_os_string();
|
||||
|
||||
assert!(
|
||||
// It's only save to set the input_filename, if it does not overwrite an existing one.
|
||||
(moved.input_filename.is_none() || moved.input_filename.unwrap() == path_as_string),
|
||||
"Already specified an input file under a different name. This is not supported"
|
||||
);
|
||||
|
||||
moved.input_filename = Some(path_as_string);
|
||||
moved
|
||||
}
|
||||
|
||||
/// Place the input at this position and set the default filename for the input.
|
||||
#[must_use]
|
||||
/// The filename includes the PID of the fuzzer to ensure that no two fuzzers write to the same file
|
||||
pub fn arg_input_file_std(self) -> Self {
|
||||
self.arg_input_file(get_unique_std_input_file())
|
||||
}
|
||||
|
||||
/// If `debug_child` is set, the child will print to `stdout`/`stderr`.
|
||||
#[must_use]
|
||||
pub fn debug_child(mut self, debug_child: bool) -> Self {
|
||||
@ -1453,12 +1345,11 @@ impl<'a> ForkserverExecutorBuilder<'a, NopTargetBytesConverter<BytesInput>, Unix
|
||||
arguments: vec![],
|
||||
envs: vec![],
|
||||
debug_child: false,
|
||||
use_stdin: false,
|
||||
uses_shmem_testcase: false,
|
||||
is_persistent: false,
|
||||
is_deferred_frksrv: false,
|
||||
autotokens: None,
|
||||
input_filename: None,
|
||||
input_location: InputLocation::StdIn,
|
||||
shmem_provider: None,
|
||||
map_size: None,
|
||||
max_input_size: MAX_INPUT_SIZE_DEFAULT,
|
||||
@ -1487,12 +1378,11 @@ impl<'a, TC> ForkserverExecutorBuilder<'a, TC, UnixShMemProvider> {
|
||||
arguments: self.arguments,
|
||||
envs: self.envs,
|
||||
debug_child: self.debug_child,
|
||||
use_stdin: self.use_stdin,
|
||||
uses_shmem_testcase: self.uses_shmem_testcase,
|
||||
is_persistent: self.is_persistent,
|
||||
is_deferred_frksrv: self.is_deferred_frksrv,
|
||||
autotokens: self.autotokens,
|
||||
input_filename: self.input_filename,
|
||||
input_location: InputLocation::StdIn,
|
||||
map_size: self.map_size,
|
||||
max_input_size: self.max_input_size,
|
||||
min_input_size: self.min_input_size,
|
||||
@ -1520,12 +1410,11 @@ impl<'a, TC, SP> ForkserverExecutorBuilder<'a, TC, SP> {
|
||||
arguments: self.arguments,
|
||||
envs: self.envs,
|
||||
debug_child: self.debug_child,
|
||||
use_stdin: self.use_stdin,
|
||||
uses_shmem_testcase: self.uses_shmem_testcase,
|
||||
is_persistent: self.is_persistent,
|
||||
is_deferred_frksrv: self.is_deferred_frksrv,
|
||||
autotokens: self.autotokens,
|
||||
input_filename: self.input_filename,
|
||||
input_location: InputLocation::StdIn,
|
||||
map_size: self.map_size,
|
||||
max_input_size: self.max_input_size,
|
||||
min_input_size: self.min_input_size,
|
||||
@ -1600,7 +1489,7 @@ mod tests {
|
||||
use std::ffi::OsString;
|
||||
|
||||
use libafl_bolts::{
|
||||
AsSliceMut,
|
||||
AsSliceMut, TargetArgs,
|
||||
shmem::{ShMem, ShMemProvider, UnixShMemProvider},
|
||||
tuples::tuple_list,
|
||||
};
|
||||
|
@ -36,6 +36,7 @@ use crate::{Error, observers::Observer};
|
||||
/// };
|
||||
/// use libafl_bolts::{
|
||||
/// Named, current_nanos,
|
||||
/// TargetArgs,
|
||||
/// rands::StdRand,
|
||||
/// tuples::{Handle, Handled, MatchNameRef, tuple_list},
|
||||
/// };
|
||||
|
137
libafl_bolts/src/argparse.rs
Normal file
137
libafl_bolts/src/argparse.rs
Normal file
@ -0,0 +1,137 @@
|
||||
//! Parse command line argument like AFL, then put it in a C-compatible way
|
||||
use alloc::{boxed::Box, ffi::CString, vec::Vec};
|
||||
use core::{
|
||||
ffi::{c_char, c_int},
|
||||
pin::Pin,
|
||||
};
|
||||
use std::{ffi::OsString, os::unix::ffi::OsStrExt};
|
||||
|
||||
use crate::{Error, InputLocation, TargetArgs};
|
||||
|
||||
/// For creating an C-compatible argument
|
||||
#[derive(Debug)]
|
||||
pub struct CMainArgsBuilder {
|
||||
program: Option<OsString>,
|
||||
input_location: InputLocation,
|
||||
envs: Vec<(OsString, OsString)>,
|
||||
args: Vec<OsString>,
|
||||
}
|
||||
|
||||
impl TargetArgs for CMainArgsBuilder {
|
||||
fn arguments_ref(&self) -> &Vec<OsString> {
|
||||
&self.args
|
||||
}
|
||||
|
||||
fn arguments_mut(&mut self) -> &mut Vec<OsString> {
|
||||
&mut self.args
|
||||
}
|
||||
|
||||
fn input_location_ref(&self) -> &InputLocation {
|
||||
&self.input_location
|
||||
}
|
||||
|
||||
fn input_location_mut(&mut self) -> &mut InputLocation {
|
||||
&mut self.input_location
|
||||
}
|
||||
|
||||
fn envs_ref(&self) -> &Vec<(OsString, OsString)> {
|
||||
&self.envs
|
||||
}
|
||||
|
||||
fn envs_mut(&mut self) -> &mut Vec<(OsString, OsString)> {
|
||||
&mut self.envs
|
||||
}
|
||||
|
||||
fn program_ref(&self) -> &Option<OsString> {
|
||||
&self.program
|
||||
}
|
||||
|
||||
fn program_mut(&mut self) -> &mut Option<OsString> {
|
||||
&mut self.program
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for CMainArgsBuilder {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl CMainArgsBuilder {
|
||||
/// Constructor
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
program: None,
|
||||
input_location: InputLocation::StdIn,
|
||||
envs: Vec::new(),
|
||||
args: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Build it
|
||||
pub fn build(&self) -> Result<CMainArgs, Error> {
|
||||
let mut argv: Vec<Pin<Box<CString>>> = Vec::new();
|
||||
|
||||
if let Some(program) = &self.program {
|
||||
argv.push(Box::pin(CString::new(program.as_bytes()).unwrap()));
|
||||
} else {
|
||||
return Err(Error::illegal_argument("Program not specified"));
|
||||
}
|
||||
|
||||
for args in &self.args {
|
||||
argv.push(Box::pin(CString::new(args.as_bytes()).unwrap()));
|
||||
}
|
||||
|
||||
let mut argv_ptr: Vec<*const c_char> = argv.iter().map(|arg| arg.as_ptr()).collect();
|
||||
argv_ptr.push(core::ptr::null());
|
||||
|
||||
Ok(CMainArgs {
|
||||
use_stdin: self.use_stdin(),
|
||||
argv,
|
||||
argv_ptr,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// For creating an C-compatible argument
|
||||
#[derive(Debug)]
|
||||
#[allow(dead_code)]
|
||||
pub struct CMainArgs {
|
||||
use_stdin: bool,
|
||||
/// This guys have to sit here, else Rust will free them
|
||||
argv: Vec<Pin<Box<CString>>>,
|
||||
argv_ptr: Vec<*const c_char>,
|
||||
}
|
||||
|
||||
// From https://gist.github.com/TrinityCoder/793c097b5a4ab25b8fabf5cd67e92f05
|
||||
impl CMainArgs {
|
||||
/// If stdin is used for this or no
|
||||
#[must_use]
|
||||
pub fn use_stdin(&self) -> bool {
|
||||
self.use_stdin
|
||||
}
|
||||
|
||||
/// Returns the C language's `argv` (`*const *const c_char`).
|
||||
#[must_use]
|
||||
pub fn argv(&self) -> *const *const c_char {
|
||||
// println!("{:#?}", self.argv_ptr);
|
||||
self.argv_ptr.as_ptr()
|
||||
}
|
||||
|
||||
/// Returns the C language's `argv[0]` (`*const c_char`).
|
||||
/// On x64 you would pass this to Rsi before starting emulation
|
||||
/// Like: `qemu.write_reg(Regs::Rsi, main_args.argv() as u64).unwrap();`
|
||||
#[must_use]
|
||||
pub fn argv0(&self) -> *const c_char {
|
||||
self.argv_ptr[0]
|
||||
}
|
||||
|
||||
/// Gets total number of args.
|
||||
/// On x64 you would pass this to Rdi before starting emulation
|
||||
/// Like: `qemu.write_reg(Regs::Rdi, main_args.argc() as u64).unwrap();`
|
||||
#[must_use]
|
||||
pub fn argc(&self) -> c_int {
|
||||
(self.argv_ptr.len() - 1).try_into().unwrap()
|
||||
}
|
||||
}
|
@ -1,222 +0,0 @@
|
||||
//! Parse command line argument like AFL, then put it in a C-compatible way
|
||||
use alloc::{borrow::ToOwned, boxed::Box, ffi::CString, vec::Vec};
|
||||
use core::{
|
||||
ffi::{c_char, c_int},
|
||||
pin::Pin,
|
||||
};
|
||||
use std::{
|
||||
ffi::{OsStr, OsString},
|
||||
os::unix::ffi::OsStrExt,
|
||||
path::Path,
|
||||
};
|
||||
|
||||
use crate::{Error, fs::get_unique_std_input_file};
|
||||
|
||||
/// For creating an C-compatible argument
|
||||
#[derive(Debug)]
|
||||
pub struct CMainArgsBuilder {
|
||||
use_stdin: bool,
|
||||
program: Option<OsString>,
|
||||
input_filename: Option<OsString>,
|
||||
args: Vec<OsString>,
|
||||
}
|
||||
|
||||
impl Default for CMainArgsBuilder {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl CMainArgsBuilder {
|
||||
/// Constructor
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
program: None,
|
||||
use_stdin: false,
|
||||
input_filename: None,
|
||||
args: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// The harness
|
||||
#[must_use]
|
||||
pub fn program<O>(mut self, program: O) -> Self
|
||||
where
|
||||
O: AsRef<OsStr>,
|
||||
{
|
||||
self.program = Some(program.as_ref().to_owned());
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds an argument to the harness's commandline
|
||||
///
|
||||
/// You may want to use `parse_afl_cmdline` if you're going to pass `@@`
|
||||
/// represents the input file generated by the fuzzer (similar to the `afl-fuzz` command line).
|
||||
#[must_use]
|
||||
pub fn arg<O>(mut self, arg: O) -> Self
|
||||
where
|
||||
O: AsRef<OsStr>,
|
||||
{
|
||||
self.args.push(arg.as_ref().to_owned());
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds arguments to the harness's commandline
|
||||
///
|
||||
/// You may want to use `parse_afl_cmdline` if you're going to pass `@@`
|
||||
/// represents the input file generated by the fuzzer (similar to the `afl-fuzz` command line).
|
||||
#[must_use]
|
||||
pub fn args<IT, O>(mut self, args: IT) -> Self
|
||||
where
|
||||
IT: IntoIterator<Item = O>,
|
||||
O: AsRef<OsStr>,
|
||||
{
|
||||
let mut res = vec![];
|
||||
for arg in args {
|
||||
res.push(arg.as_ref().to_owned());
|
||||
}
|
||||
self.args.append(&mut res);
|
||||
self
|
||||
}
|
||||
|
||||
/// Place the input at this position and set the filename for the input.
|
||||
///
|
||||
/// Note: If you use this, you should ensure that there is only one instance using this
|
||||
/// file at any given time.
|
||||
#[must_use]
|
||||
pub fn arg_input_file<P: AsRef<Path>>(self, path: P) -> Self {
|
||||
let mut moved = self.arg(path.as_ref());
|
||||
|
||||
let path_as_string = path.as_ref().as_os_str().to_os_string();
|
||||
|
||||
assert!(
|
||||
// It's only save to set the input_filename, if it does not overwrite an existing one.
|
||||
(moved.input_filename.is_none() || moved.input_filename.unwrap() == path_as_string),
|
||||
"Already specified an input file under a different name. This is not supported"
|
||||
);
|
||||
|
||||
moved.input_filename = Some(path_as_string);
|
||||
moved
|
||||
}
|
||||
|
||||
/// Place the input at this position and set the default filename for the input.
|
||||
#[must_use]
|
||||
/// The filename includes the PID of the fuzzer to ensure that no two fuzzers write to the same file
|
||||
pub fn arg_input_file_std(self) -> Self {
|
||||
self.arg_input_file(get_unique_std_input_file())
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
/// Parse afl style command line
|
||||
///
|
||||
/// Replaces `@@` with the path to the input file generated by the fuzzer. If `@@` is omitted,
|
||||
/// `stdin` is used to pass the test case instead.
|
||||
///
|
||||
/// Interprets the first argument as the path to the program as long as it is not set yet.
|
||||
/// You have to omit the program path in case you have set it already. Otherwise
|
||||
/// it will be interpreted as a regular argument, leading to probably unintended results.
|
||||
pub fn parse_afl_cmdline<IT, O>(self, args: IT) -> Self
|
||||
where
|
||||
IT: IntoIterator<Item = O>,
|
||||
O: AsRef<OsStr>,
|
||||
{
|
||||
let mut moved = self;
|
||||
|
||||
let mut use_arg_0_as_program = false;
|
||||
if moved.program.is_none() {
|
||||
use_arg_0_as_program = true;
|
||||
}
|
||||
|
||||
for item in args {
|
||||
if use_arg_0_as_program {
|
||||
moved = moved.program(item);
|
||||
// After the program has been set, unset `use_arg_0_as_program` to treat all
|
||||
// subsequent arguments as regular arguments
|
||||
use_arg_0_as_program = false;
|
||||
} else if item.as_ref() == "@@" {
|
||||
match &moved.input_filename.clone() {
|
||||
Some(name) => {
|
||||
// If the input file name has been modified, use this one
|
||||
moved = moved.arg_input_file(name);
|
||||
}
|
||||
_ => {
|
||||
moved = moved.arg_input_file_std();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
moved = moved.arg(item);
|
||||
}
|
||||
}
|
||||
|
||||
// If we have not set an input file, use stdin as it is AFLs default
|
||||
moved.use_stdin = moved.input_filename.is_none();
|
||||
moved
|
||||
}
|
||||
|
||||
/// Build it
|
||||
pub fn build(&self) -> Result<CMainArgs, Error> {
|
||||
let mut argv: Vec<Pin<Box<CString>>> = Vec::new();
|
||||
|
||||
if let Some(program) = &self.program {
|
||||
argv.push(Box::pin(CString::new(program.as_bytes()).unwrap()));
|
||||
} else {
|
||||
return Err(Error::illegal_argument("Program not specified"));
|
||||
}
|
||||
|
||||
for args in &self.args {
|
||||
argv.push(Box::pin(CString::new(args.as_bytes()).unwrap()));
|
||||
}
|
||||
|
||||
let mut argv_ptr: Vec<*const c_char> = argv.iter().map(|arg| arg.as_ptr()).collect();
|
||||
argv_ptr.push(core::ptr::null());
|
||||
|
||||
Ok(CMainArgs {
|
||||
use_stdin: self.use_stdin,
|
||||
argv,
|
||||
argv_ptr,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// For creating an C-compatible argument
|
||||
#[derive(Debug)]
|
||||
#[allow(dead_code)]
|
||||
pub struct CMainArgs {
|
||||
use_stdin: bool,
|
||||
/// This guys have to sit here, else Rust will free them
|
||||
argv: Vec<Pin<Box<CString>>>,
|
||||
argv_ptr: Vec<*const c_char>,
|
||||
}
|
||||
|
||||
// From https://gist.github.com/TrinityCoder/793c097b5a4ab25b8fabf5cd67e92f05
|
||||
impl CMainArgs {
|
||||
/// If stdin is used for this or no
|
||||
#[must_use]
|
||||
pub fn use_stdin(&self) -> bool {
|
||||
self.use_stdin
|
||||
}
|
||||
|
||||
/// Returns the C language's `argv` (`*const *const c_char`).
|
||||
#[must_use]
|
||||
pub fn argv(&self) -> *const *const c_char {
|
||||
// println!("{:#?}", self.argv_ptr);
|
||||
self.argv_ptr.as_ptr()
|
||||
}
|
||||
|
||||
/// Returns the C language's `argv[0]` (`*const c_char`).
|
||||
/// On x64 you would pass this to Rsi before starting emulation
|
||||
/// Like: `qemu.write_reg(Regs::Rsi, main_args.argv() as u64).unwrap();`
|
||||
#[must_use]
|
||||
pub fn argv0(&self) -> *const c_char {
|
||||
self.argv_ptr[0]
|
||||
}
|
||||
|
||||
/// Gets total number of args.
|
||||
/// On x64 you would pass this to Rdi before starting emulation
|
||||
/// Like: `qemu.write_reg(Regs::Rdi, main_args.argc() as u64).unwrap();`
|
||||
#[must_use]
|
||||
pub fn argc(&self) -> c_int {
|
||||
(self.argv_ptr.len() - 1).try_into().unwrap()
|
||||
}
|
||||
}
|
@ -113,9 +113,14 @@ pub mod subrange;
|
||||
pub mod tuples;
|
||||
|
||||
#[cfg(all(feature = "std", unix))]
|
||||
pub mod cargs;
|
||||
pub mod argparse;
|
||||
#[cfg(all(feature = "std", unix))]
|
||||
pub use cargs::*;
|
||||
pub use argparse::*;
|
||||
|
||||
#[cfg(all(feature = "std", unix))]
|
||||
pub mod target_args;
|
||||
#[cfg(all(feature = "std", unix))]
|
||||
pub use target_args::*;
|
||||
|
||||
/// The purpose of this module is to alleviate imports of the bolts by adding a glob import.
|
||||
#[cfg(feature = "prelude")]
|
||||
|
220
libafl_bolts/src/target_args.rs
Normal file
220
libafl_bolts/src/target_args.rs
Normal file
@ -0,0 +1,220 @@
|
||||
//! Shared implementation of afl style arguments
|
||||
|
||||
use alloc::{borrow::ToOwned, vec::Vec};
|
||||
use std::{
|
||||
ffi::{OsStr, OsString},
|
||||
path::Path,
|
||||
};
|
||||
|
||||
use crate::fs::{InputFile, get_unique_std_input_file};
|
||||
|
||||
/// How to deliver input to an external program
|
||||
/// `StdIn`: The target reads from stdin
|
||||
/// `File`: The target reads from the specified [`InputFile`]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
||||
pub enum InputLocation {
|
||||
/// Mutate a commandline argument to deliver an input
|
||||
Arg {
|
||||
/// The offset of the argument to mutate
|
||||
argnum: usize,
|
||||
},
|
||||
/// Deliver input via `StdIn`
|
||||
#[default]
|
||||
StdIn,
|
||||
/// Deliver the input via the specified [`InputFile`]
|
||||
/// You can use specify [`InputFile::create(INPUTFILE_STD)`] to use a default filename.
|
||||
File {
|
||||
/// The file to write input to. The target should read input from this location.
|
||||
out_file: InputFile,
|
||||
},
|
||||
}
|
||||
|
||||
/// The main implementation trait of afl style arguments handling
|
||||
pub trait TargetArgs: Sized {
|
||||
/// Gets the arguments
|
||||
fn arguments_ref(&self) -> &Vec<OsString>;
|
||||
/// Gets the mutable arguments
|
||||
fn arguments_mut(&mut self) -> &mut Vec<OsString>;
|
||||
|
||||
/// Gets the main program
|
||||
fn program_ref(&self) -> &Option<OsString>;
|
||||
/// Gets the mutable main program
|
||||
fn program_mut(&mut self) -> &mut Option<OsString>;
|
||||
|
||||
/// Gets the input file
|
||||
fn input_location_ref(&self) -> &InputLocation;
|
||||
/// Gets the mutable input file
|
||||
fn input_location_mut(&mut self) -> &mut InputLocation;
|
||||
|
||||
/// Get the environments
|
||||
fn envs_ref(&self) -> &Vec<(OsString, OsString)>;
|
||||
/// Get the mutable environments
|
||||
fn envs_mut(&mut self) -> &mut Vec<(OsString, OsString)>;
|
||||
|
||||
/// Adds an environmental var to the harness's commandline
|
||||
#[must_use]
|
||||
fn env<K, V>(mut self, key: K, val: V) -> Self
|
||||
where
|
||||
K: AsRef<OsStr>,
|
||||
V: AsRef<OsStr>,
|
||||
{
|
||||
self.envs_mut()
|
||||
.push((key.as_ref().to_owned(), val.as_ref().to_owned()));
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds environmental vars to the harness's commandline
|
||||
#[must_use]
|
||||
fn envs<IT, K, V>(mut self, vars: IT) -> Self
|
||||
where
|
||||
IT: IntoIterator<Item = (K, V)>,
|
||||
K: AsRef<OsStr>,
|
||||
V: AsRef<OsStr>,
|
||||
{
|
||||
let mut res = vec![];
|
||||
for (ref key, ref val) in vars {
|
||||
res.push((key.as_ref().to_owned(), val.as_ref().to_owned()));
|
||||
}
|
||||
self.envs_mut().append(&mut res);
|
||||
self
|
||||
}
|
||||
|
||||
/// If use stdin
|
||||
#[must_use]
|
||||
fn use_stdin(&self) -> bool {
|
||||
matches!(self.input_location_ref(), InputLocation::StdIn)
|
||||
}
|
||||
|
||||
/// Set input
|
||||
#[must_use]
|
||||
fn input(mut self, input: InputLocation) -> Self {
|
||||
*self.input_location_mut() = input;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the input mode to [`InputLocation::Arg`] and uses the current arg offset as `argnum`.
|
||||
/// During execution, at input will be provided _as argument_ at this position.
|
||||
/// Use [`Self::arg_input_file_std`] if you want to provide the input as a file instead.
|
||||
#[must_use]
|
||||
fn arg_input_arg(mut self) -> Self {
|
||||
let argnum = self.arguments_ref().len();
|
||||
self = self.input(InputLocation::Arg { argnum });
|
||||
// Placeholder arg that gets replaced with the input name later.
|
||||
self = self.arg("PLACEHOLDER");
|
||||
self
|
||||
}
|
||||
|
||||
/// Place the input at this position and set the filename for the input.
|
||||
///
|
||||
/// Note: If you use this, you should ensure that there is only one instance using this
|
||||
/// file at any given time.
|
||||
#[must_use]
|
||||
fn arg_input_file<P: AsRef<Path>>(self, path: P) -> Self {
|
||||
let mut moved = self.arg(path.as_ref());
|
||||
assert!(
|
||||
match moved.input_location_ref() {
|
||||
InputLocation::File { out_file } => out_file.path.as_path() == path.as_ref(),
|
||||
InputLocation::StdIn => true,
|
||||
InputLocation::Arg { argnum: _ } => false,
|
||||
},
|
||||
"Already specified an input file under a different name. This is not supported"
|
||||
);
|
||||
let out_file = InputFile::create(path).unwrap();
|
||||
moved = moved.input(InputLocation::File { out_file });
|
||||
moved
|
||||
}
|
||||
|
||||
/// Place the input at this position and set the default filename for the input.
|
||||
#[must_use]
|
||||
/// The filename includes the PID of the fuzzer to ensure that no two fuzzers write to the same file
|
||||
fn arg_input_file_std(self) -> Self {
|
||||
self.arg_input_file(get_unique_std_input_file())
|
||||
}
|
||||
|
||||
/// The harness
|
||||
#[must_use]
|
||||
fn program<O>(mut self, program: O) -> Self
|
||||
where
|
||||
O: AsRef<OsStr>,
|
||||
{
|
||||
*self.program_mut() = Some(program.as_ref().to_owned());
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds an argument to the harness's commandline
|
||||
///
|
||||
/// You may want to use `parse_afl_cmdline` if you're going to pass `@@`
|
||||
/// represents the input file generated by the fuzzer (similar to the `afl-fuzz` command line).
|
||||
#[must_use]
|
||||
fn arg<O>(mut self, arg: O) -> Self
|
||||
where
|
||||
O: AsRef<OsStr>,
|
||||
{
|
||||
self.arguments_mut().push(arg.as_ref().to_owned());
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds arguments to the harness's commandline
|
||||
///
|
||||
/// You may want to use `parse_afl_cmdline` if you're going to pass `@@`
|
||||
/// represents the input file generated by the fuzzer (similar to the `afl-fuzz` command line).
|
||||
#[must_use]
|
||||
fn args<IT, O>(mut self, args: IT) -> Self
|
||||
where
|
||||
IT: IntoIterator<Item = O>,
|
||||
O: AsRef<OsStr>,
|
||||
{
|
||||
let mut res = vec![];
|
||||
for arg in args {
|
||||
res.push(arg.as_ref().to_owned());
|
||||
}
|
||||
self.arguments_mut().append(&mut res);
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
/// Parse afl style command line
|
||||
///
|
||||
/// Replaces `@@` with the path to the input file generated by the fuzzer. If `@@` is omitted,
|
||||
/// `stdin` is used to pass the test case instead.
|
||||
///
|
||||
/// Interprets the first argument as the path to the program as long as it is not set yet.
|
||||
/// You have to omit the program path in case you have set it already. Otherwise
|
||||
/// it will be interpreted as a regular argument, leading to probably unintended results.
|
||||
fn parse_afl_cmdline<IT, O>(self, args: IT) -> Self
|
||||
where
|
||||
IT: IntoIterator<Item = O>,
|
||||
O: AsRef<OsStr>,
|
||||
{
|
||||
let mut moved = self;
|
||||
|
||||
let mut use_arg_0_as_program = false;
|
||||
if moved.program_ref().is_none() {
|
||||
use_arg_0_as_program = true;
|
||||
}
|
||||
|
||||
for item in args {
|
||||
if use_arg_0_as_program {
|
||||
moved = moved.program(item);
|
||||
// After the program has been set, unset `use_arg_0_as_program` to treat all
|
||||
// subsequent arguments as regular arguments
|
||||
use_arg_0_as_program = false;
|
||||
} else if item.as_ref() == "@@" {
|
||||
match moved.input_location_ref().clone() {
|
||||
InputLocation::File { out_file } => {
|
||||
// If the input file name has been modified, use this one
|
||||
moved = moved.arg_input_file(&out_file.path);
|
||||
}
|
||||
_ => {
|
||||
moved = moved.arg_input_file_std();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
moved = moved.arg(item);
|
||||
}
|
||||
}
|
||||
|
||||
// If we have not set an input file, use stdin as it is AFLs default
|
||||
moved
|
||||
}
|
||||
}
|
@ -24,7 +24,7 @@ use libafl::{
|
||||
state::{HasCorpus, StdState},
|
||||
};
|
||||
use libafl_bolts::{
|
||||
AsSliceMut,
|
||||
AsSliceMut, TargetArgs,
|
||||
core_affinity::Cores,
|
||||
nonzero,
|
||||
rands::StdRand,
|
||||
|
Loading…
x
Reference in New Issue
Block a user