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,
|
rands::StdRand,
|
||||||
shmem::{ShMem, ShMemProvider},
|
shmem::{ShMem, ShMemProvider},
|
||||||
tuples::tuple_list,
|
tuples::tuple_list,
|
||||||
AsSliceMut,
|
AsSliceMut, TargetArgs,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
|
@ -22,7 +22,7 @@ use libafl_bolts::{
|
|||||||
rands::StdRand,
|
rands::StdRand,
|
||||||
shmem::{ShMem, ShMemProvider, UnixShMemProvider},
|
shmem::{ShMem, ShMemProvider, UnixShMemProvider},
|
||||||
tuples::{tuple_list, Handled, Merge},
|
tuples::{tuple_list, Handled, Merge},
|
||||||
AsSliceMut, Truncate,
|
AsSliceMut, TargetArgs, Truncate,
|
||||||
};
|
};
|
||||||
use libafl_targets::EDGES_MAP_DEFAULT_SIZE;
|
use libafl_targets::EDGES_MAP_DEFAULT_SIZE;
|
||||||
use nix::sys::signal::Signal;
|
use nix::sys::signal::Signal;
|
||||||
|
@ -19,7 +19,7 @@ use libafl::{
|
|||||||
state::{HasCorpus, StdState},
|
state::{HasCorpus, StdState},
|
||||||
};
|
};
|
||||||
use libafl_bolts::{
|
use libafl_bolts::{
|
||||||
AsSliceMut, Truncate, current_nanos,
|
AsSliceMut, TargetArgs, Truncate, current_nanos,
|
||||||
rands::StdRand,
|
rands::StdRand,
|
||||||
shmem::{ShMem, ShMemProvider, UnixShMemProvider},
|
shmem::{ShMem, ShMemProvider, UnixShMemProvider},
|
||||||
tuples::{Handled, Merge, tuple_list},
|
tuples::{Handled, Merge, tuple_list},
|
||||||
|
@ -38,7 +38,7 @@ use libafl_bolts::{
|
|||||||
rands::StdRand,
|
rands::StdRand,
|
||||||
shmem::{ShMem, ShMemProvider, UnixShMemProvider},
|
shmem::{ShMem, ShMemProvider, UnixShMemProvider},
|
||||||
tuples::{tuple_list, Merge},
|
tuples::{tuple_list, Merge},
|
||||||
AsSliceMut,
|
AsSliceMut, TargetArgs,
|
||||||
};
|
};
|
||||||
use libafl_targets::cmps::AFLppCmpLogMap;
|
use libafl_targets::cmps::AFLppCmpLogMap;
|
||||||
use nix::sys::signal::Signal;
|
use nix::sys::signal::Signal;
|
||||||
|
@ -37,7 +37,7 @@ use libafl_bolts::{
|
|||||||
rands::StdRand,
|
rands::StdRand,
|
||||||
shmem::{ShMem, ShMemProvider, UnixShMemProvider},
|
shmem::{ShMem, ShMemProvider, UnixShMemProvider},
|
||||||
tuples::{tuple_list, Handled, Merge},
|
tuples::{tuple_list, Handled, Merge},
|
||||||
AsSliceMut,
|
AsSliceMut, TargetArgs,
|
||||||
};
|
};
|
||||||
use libafl_targets::{
|
use libafl_targets::{
|
||||||
cmps::{observers::AFLppCmpLogObserver, stages::AFLppCmplogTracingStage},
|
cmps::{observers::AFLppCmpLogObserver, stages::AFLppCmplogTracingStage},
|
||||||
|
@ -38,7 +38,7 @@ use libafl_bolts::{
|
|||||||
rands::StdRand,
|
rands::StdRand,
|
||||||
shmem::{ShMem, ShMemProvider, UnixShMemProvider},
|
shmem::{ShMem, ShMemProvider, UnixShMemProvider},
|
||||||
tuples::{tuple_list, Handled, Merge},
|
tuples::{tuple_list, Handled, Merge},
|
||||||
AsSliceMut,
|
AsSliceMut, TargetArgs,
|
||||||
};
|
};
|
||||||
use libafl_targets::cmps::AFLppCmpLogMap;
|
use libafl_targets::cmps::AFLppCmpLogMap;
|
||||||
use nix::sys::signal::Signal;
|
use nix::sys::signal::Signal;
|
||||||
|
@ -51,7 +51,7 @@ use libafl_bolts::{
|
|||||||
rands::StdRand,
|
rands::StdRand,
|
||||||
shmem::{ShMem, ShMemProvider, UnixShMemProvider},
|
shmem::{ShMem, ShMemProvider, UnixShMemProvider},
|
||||||
tuples::{tuple_list, Handled, Merge},
|
tuples::{tuple_list, Handled, Merge},
|
||||||
AsSliceMut,
|
AsSliceMut, TargetArgs,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "nyx")]
|
#[cfg(feature = "nyx")]
|
||||||
use libafl_nyx::{executor::NyxExecutor, helper::NyxHelper, settings::NyxSettings};
|
use libafl_nyx::{executor::NyxExecutor, helper::NyxHelper, settings::NyxSettings};
|
||||||
|
@ -29,7 +29,7 @@ use libafl_bolts::{
|
|||||||
rands::StdRand,
|
rands::StdRand,
|
||||||
shmem::{ShMem, ShMemProvider, UnixShMemProvider},
|
shmem::{ShMem, ShMemProvider, UnixShMemProvider},
|
||||||
tuples::{tuple_list, Handled},
|
tuples::{tuple_list, Handled},
|
||||||
AsSliceMut, Truncate,
|
AsSliceMut, TargetArgs, Truncate,
|
||||||
};
|
};
|
||||||
use nix::sys::signal::Signal;
|
use nix::sys::signal::Signal;
|
||||||
|
|
||||||
|
@ -23,8 +23,7 @@ use std::{
|
|||||||
#[cfg(all(feature = "intel_pt", target_os = "linux"))]
|
#[cfg(all(feature = "intel_pt", target_os = "linux"))]
|
||||||
use libafl_bolts::core_affinity::CoreId;
|
use libafl_bolts::core_affinity::CoreId;
|
||||||
use libafl_bolts::{
|
use libafl_bolts::{
|
||||||
AsSlice,
|
AsSlice, InputLocation, TargetArgs,
|
||||||
fs::{InputFile, get_unique_std_input_file},
|
|
||||||
tuples::{Handle, MatchName, RefIndexable},
|
tuples::{Handle, MatchName, RefIndexable},
|
||||||
};
|
};
|
||||||
#[cfg(all(feature = "intel_pt", target_os = "linux"))]
|
#[cfg(all(feature = "intel_pt", target_os = "linux"))]
|
||||||
@ -59,27 +58,6 @@ use crate::{
|
|||||||
std::borrow::ToOwned,
|
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
|
/// 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.
|
||||||
@ -530,6 +508,40 @@ pub struct CommandExecutorBuilder {
|
|||||||
timeout: Duration,
|
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 {
|
impl Default for CommandExecutorBuilder {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new()
|
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
|
/// Sets the stdout observer
|
||||||
pub fn stdout_observer(&mut self, stdout: Handle<StdOutObserver>) -> &mut Self {
|
pub fn stdout_observer(&mut self, stdout: Handle<StdOutObserver>) -> &mut Self {
|
||||||
self.stdout = Some(stdout);
|
self.stdout = Some(stdout);
|
||||||
@ -599,68 +577,6 @@ impl CommandExecutorBuilder {
|
|||||||
self
|
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.
|
/// Sets the working directory for the child process.
|
||||||
pub fn current_dir<P: AsRef<Path>>(&mut self, dir: P) -> &mut CommandExecutorBuilder {
|
pub fn current_dir<P: AsRef<Path>>(&mut self, dir: P) -> &mut CommandExecutorBuilder {
|
||||||
self.cwd = Some(dir.as_ref().to_owned());
|
self.cwd = Some(dir.as_ref().to_owned());
|
||||||
@ -865,6 +781,8 @@ fn waitpid_filtered(pid: Pid, options: Option<WaitPidFlag>) -> Result<WaitStatus
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use libafl_bolts::TargetArgs;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
events::SimpleEventManager,
|
events::SimpleEventManager,
|
||||||
executors::{
|
executors::{
|
||||||
@ -885,8 +803,7 @@ mod tests {
|
|||||||
log::info!("{status}");
|
log::info!("{status}");
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let mut executor = CommandExecutor::builder();
|
let executor = CommandExecutor::builder()
|
||||||
executor
|
|
||||||
.program("ls")
|
.program("ls")
|
||||||
.input(InputLocation::Arg { argnum: 0 });
|
.input(InputLocation::Arg { argnum: 0 });
|
||||||
let executor = executor.build(());
|
let executor = executor.build(());
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
//! Expose an `Executor` based on a `Forkserver` in order to execute AFL/AFL++ binaries
|
//! 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::{
|
use core::{
|
||||||
fmt::{self, Debug, Formatter},
|
fmt::{self, Debug, Formatter},
|
||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
@ -8,18 +8,17 @@ use core::{
|
|||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
env,
|
env,
|
||||||
ffi::{OsStr, OsString},
|
ffi::OsString,
|
||||||
io::{self, ErrorKind, Read, Write},
|
io::{self, ErrorKind, Read, Write},
|
||||||
os::{
|
os::{
|
||||||
fd::{AsRawFd, BorrowedFd},
|
fd::{AsRawFd, BorrowedFd},
|
||||||
unix::{io::RawFd, process::CommandExt},
|
unix::{io::RawFd, process::CommandExt},
|
||||||
},
|
},
|
||||||
path::Path,
|
|
||||||
process::{Child, Command, Stdio},
|
process::{Child, Command, Stdio},
|
||||||
};
|
};
|
||||||
|
|
||||||
use libafl_bolts::{
|
use libafl_bolts::{
|
||||||
AsSlice, AsSliceMut, Truncate,
|
AsSlice, AsSliceMut, InputLocation, TargetArgs, Truncate,
|
||||||
fs::{InputFile, get_unique_std_input_file},
|
fs::{InputFile, get_unique_std_input_file},
|
||||||
os::{dup2, pipes::Pipe},
|
os::{dup2, pipes::Pipe},
|
||||||
ownedref::OwnedSlice,
|
ownedref::OwnedSlice,
|
||||||
@ -777,12 +776,11 @@ pub struct ForkserverExecutorBuilder<'a, TC, SP> {
|
|||||||
arguments: Vec<OsString>,
|
arguments: Vec<OsString>,
|
||||||
envs: Vec<(OsString, OsString)>,
|
envs: Vec<(OsString, OsString)>,
|
||||||
debug_child: bool,
|
debug_child: bool,
|
||||||
use_stdin: bool,
|
|
||||||
uses_shmem_testcase: bool,
|
uses_shmem_testcase: bool,
|
||||||
is_persistent: bool,
|
is_persistent: bool,
|
||||||
is_deferred_frksrv: bool,
|
is_deferred_frksrv: bool,
|
||||||
autotokens: Option<&'a mut Tokens>,
|
autotokens: Option<&'a mut Tokens>,
|
||||||
input_filename: Option<OsString>,
|
input_location: InputLocation,
|
||||||
shmem_provider: Option<&'a mut SP>,
|
shmem_provider: Option<&'a mut SP>,
|
||||||
max_input_size: usize,
|
max_input_size: usize,
|
||||||
min_input_size: usize,
|
min_input_size: usize,
|
||||||
@ -795,6 +793,43 @@ pub struct ForkserverExecutorBuilder<'a, TC, SP> {
|
|||||||
target_bytes_converter: TC,
|
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>
|
impl<'a, TC, SHM, SP> ForkserverExecutorBuilder<'a, TC, SP>
|
||||||
where
|
where
|
||||||
SHM: ShMem,
|
SHM: ShMem,
|
||||||
@ -821,7 +856,7 @@ where
|
|||||||
"ForkserverExecutor: program: {:?}, arguments: {:?}, use_stdin: {:?}",
|
"ForkserverExecutor: program: {:?}, arguments: {:?}, use_stdin: {:?}",
|
||||||
target,
|
target,
|
||||||
self.arguments.clone(),
|
self.arguments.clone(),
|
||||||
self.use_stdin
|
self.use_stdin()
|
||||||
);
|
);
|
||||||
|
|
||||||
if self.uses_shmem_testcase && map.is_none() {
|
if self.uses_shmem_testcase && map.is_none() {
|
||||||
@ -887,7 +922,7 @@ where
|
|||||||
"ForkserverExecutor: program: {:?}, arguments: {:?}, use_stdin: {:?}, map_size: {:?}",
|
"ForkserverExecutor: program: {:?}, arguments: {:?}, use_stdin: {:?}, map_size: {:?}",
|
||||||
target,
|
target,
|
||||||
self.arguments.clone(),
|
self.arguments.clone(),
|
||||||
self.use_stdin,
|
self.use_stdin(),
|
||||||
self.map_size
|
self.map_size
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -933,16 +968,16 @@ where
|
|||||||
|
|
||||||
#[expect(clippy::pedantic)]
|
#[expect(clippy::pedantic)]
|
||||||
fn build_helper(&mut self) -> Result<(Forkserver, InputFile, Option<SHM>), Error> {
|
fn build_helper(&mut self) -> Result<(Forkserver, InputFile, Option<SHM>), Error> {
|
||||||
let input_filename = match &self.input_filename {
|
let input_file = match &self.input_location {
|
||||||
Some(name) => name.clone(),
|
InputLocation::StdIn => InputFile::create(OsString::from(get_unique_std_input_file()))?,
|
||||||
None => {
|
InputLocation::Arg { argnum: _ } => {
|
||||||
self.use_stdin = true;
|
return Err(Error::illegal_argument(
|
||||||
OsString::from(get_unique_std_input_file())
|
"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 {
|
let map = match &mut self.shmem_provider {
|
||||||
None => None,
|
None => None,
|
||||||
Some(provider) => {
|
Some(provider) => {
|
||||||
@ -966,7 +1001,7 @@ where
|
|||||||
self.arguments.clone(),
|
self.arguments.clone(),
|
||||||
self.envs.clone(),
|
self.envs.clone(),
|
||||||
input_file.as_raw_fd(),
|
input_file.as_raw_fd(),
|
||||||
self.use_stdin,
|
self.use_stdin(),
|
||||||
0,
|
0,
|
||||||
self.is_persistent,
|
self.is_persistent,
|
||||||
self.is_deferred_frksrv,
|
self.is_deferred_frksrv,
|
||||||
@ -1226,94 +1261,6 @@ where
|
|||||||
self
|
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
|
/// Set the max input size
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn max_input_size(mut self, size: usize) -> Self {
|
pub fn max_input_size(mut self, size: usize) -> Self {
|
||||||
@ -1328,61 +1275,6 @@ where
|
|||||||
self
|
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`.
|
/// If `debug_child` is set, the child will print to `stdout`/`stderr`.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn debug_child(mut self, debug_child: bool) -> Self {
|
pub fn debug_child(mut self, debug_child: bool) -> Self {
|
||||||
@ -1453,12 +1345,11 @@ impl<'a> ForkserverExecutorBuilder<'a, NopTargetBytesConverter<BytesInput>, Unix
|
|||||||
arguments: vec![],
|
arguments: vec![],
|
||||||
envs: vec![],
|
envs: vec![],
|
||||||
debug_child: false,
|
debug_child: false,
|
||||||
use_stdin: false,
|
|
||||||
uses_shmem_testcase: false,
|
uses_shmem_testcase: false,
|
||||||
is_persistent: false,
|
is_persistent: false,
|
||||||
is_deferred_frksrv: false,
|
is_deferred_frksrv: false,
|
||||||
autotokens: None,
|
autotokens: None,
|
||||||
input_filename: None,
|
input_location: InputLocation::StdIn,
|
||||||
shmem_provider: None,
|
shmem_provider: None,
|
||||||
map_size: None,
|
map_size: None,
|
||||||
max_input_size: MAX_INPUT_SIZE_DEFAULT,
|
max_input_size: MAX_INPUT_SIZE_DEFAULT,
|
||||||
@ -1487,12 +1378,11 @@ impl<'a, TC> ForkserverExecutorBuilder<'a, TC, UnixShMemProvider> {
|
|||||||
arguments: self.arguments,
|
arguments: self.arguments,
|
||||||
envs: self.envs,
|
envs: self.envs,
|
||||||
debug_child: self.debug_child,
|
debug_child: self.debug_child,
|
||||||
use_stdin: self.use_stdin,
|
|
||||||
uses_shmem_testcase: self.uses_shmem_testcase,
|
uses_shmem_testcase: self.uses_shmem_testcase,
|
||||||
is_persistent: self.is_persistent,
|
is_persistent: self.is_persistent,
|
||||||
is_deferred_frksrv: self.is_deferred_frksrv,
|
is_deferred_frksrv: self.is_deferred_frksrv,
|
||||||
autotokens: self.autotokens,
|
autotokens: self.autotokens,
|
||||||
input_filename: self.input_filename,
|
input_location: InputLocation::StdIn,
|
||||||
map_size: self.map_size,
|
map_size: self.map_size,
|
||||||
max_input_size: self.max_input_size,
|
max_input_size: self.max_input_size,
|
||||||
min_input_size: self.min_input_size,
|
min_input_size: self.min_input_size,
|
||||||
@ -1520,12 +1410,11 @@ impl<'a, TC, SP> ForkserverExecutorBuilder<'a, TC, SP> {
|
|||||||
arguments: self.arguments,
|
arguments: self.arguments,
|
||||||
envs: self.envs,
|
envs: self.envs,
|
||||||
debug_child: self.debug_child,
|
debug_child: self.debug_child,
|
||||||
use_stdin: self.use_stdin,
|
|
||||||
uses_shmem_testcase: self.uses_shmem_testcase,
|
uses_shmem_testcase: self.uses_shmem_testcase,
|
||||||
is_persistent: self.is_persistent,
|
is_persistent: self.is_persistent,
|
||||||
is_deferred_frksrv: self.is_deferred_frksrv,
|
is_deferred_frksrv: self.is_deferred_frksrv,
|
||||||
autotokens: self.autotokens,
|
autotokens: self.autotokens,
|
||||||
input_filename: self.input_filename,
|
input_location: InputLocation::StdIn,
|
||||||
map_size: self.map_size,
|
map_size: self.map_size,
|
||||||
max_input_size: self.max_input_size,
|
max_input_size: self.max_input_size,
|
||||||
min_input_size: self.min_input_size,
|
min_input_size: self.min_input_size,
|
||||||
@ -1600,7 +1489,7 @@ mod tests {
|
|||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
|
|
||||||
use libafl_bolts::{
|
use libafl_bolts::{
|
||||||
AsSliceMut,
|
AsSliceMut, TargetArgs,
|
||||||
shmem::{ShMem, ShMemProvider, UnixShMemProvider},
|
shmem::{ShMem, ShMemProvider, UnixShMemProvider},
|
||||||
tuples::tuple_list,
|
tuples::tuple_list,
|
||||||
};
|
};
|
||||||
|
@ -36,6 +36,7 @@ use crate::{Error, observers::Observer};
|
|||||||
/// };
|
/// };
|
||||||
/// use libafl_bolts::{
|
/// use libafl_bolts::{
|
||||||
/// Named, current_nanos,
|
/// Named, current_nanos,
|
||||||
|
/// TargetArgs,
|
||||||
/// rands::StdRand,
|
/// rands::StdRand,
|
||||||
/// tuples::{Handle, Handled, MatchNameRef, tuple_list},
|
/// 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;
|
pub mod tuples;
|
||||||
|
|
||||||
#[cfg(all(feature = "std", unix))]
|
#[cfg(all(feature = "std", unix))]
|
||||||
pub mod cargs;
|
pub mod argparse;
|
||||||
#[cfg(all(feature = "std", unix))]
|
#[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.
|
/// The purpose of this module is to alleviate imports of the bolts by adding a glob import.
|
||||||
#[cfg(feature = "prelude")]
|
#[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},
|
state::{HasCorpus, StdState},
|
||||||
};
|
};
|
||||||
use libafl_bolts::{
|
use libafl_bolts::{
|
||||||
AsSliceMut,
|
AsSliceMut, TargetArgs,
|
||||||
core_affinity::Cores,
|
core_affinity::Cores,
|
||||||
nonzero,
|
nonzero,
|
||||||
rands::StdRand,
|
rands::StdRand,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user