Allow configuring timeout for CommandExecutor (#1269)

* Allow configuring timeout for CommandExecutor

* import Duration on windows as well

* fix example fuzzers
This commit is contained in:
Arpan Kapoor 2023-05-15 16:29:06 +05:30 committed by GitHub
parent 1da621456f
commit 6d2284d8b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 46 additions and 7 deletions

View File

@ -4,6 +4,7 @@ use std::{
io::Write, io::Write,
path::PathBuf, path::PathBuf,
process::{Child, Command, Stdio}, process::{Child, Command, Stdio},
time::Duration,
}; };
use libafl::{ use libafl::{
@ -103,6 +104,10 @@ pub fn main() {
stdin.write_all(input.target_bytes().as_slice())?; stdin.write_all(input.target_bytes().as_slice())?;
Ok(child) Ok(child)
} }
fn exec_timeout(&self) -> Duration {
Duration::from_secs(5)
}
} }
let mut executor = MyExecutor { shmem_id }.into_executor(tuple_list!(observer, bt_observer)); let mut executor = MyExecutor { shmem_id }.into_executor(tuple_list!(observer, bt_observer));

View File

@ -8,6 +8,7 @@ use std::{
env, env,
path::PathBuf, path::PathBuf,
process::{Child, Command, Stdio}, process::{Child, Command, Stdio},
time::Duration,
}; };
use clap::{self, Parser}; use clap::{self, Parser};
@ -251,4 +252,8 @@ impl CommandConfigurator for MyCommandConfigurator {
.spawn() .spawn()
.expect("failed to start process")) .expect("failed to start process"))
} }
fn exec_timeout(&self) -> Duration {
Duration::from_secs(5)
}
} }

View File

@ -8,13 +8,12 @@ use core::{
use std::os::unix::ffi::OsStrExt; use std::os::unix::ffi::OsStrExt;
#[cfg(feature = "std")] #[cfg(feature = "std")]
use std::process::Child; use std::process::Child;
#[cfg(all(feature = "std", unix))]
use std::time::Duration;
use std::{ use std::{
ffi::{OsStr, OsString}, ffi::{OsStr, OsString},
io::{Read, Write}, io::{Read, Write},
path::{Path, PathBuf}, path::{Path, PathBuf},
process::{Command, Stdio}, process::{Command, Stdio},
time::Duration,
}; };
use super::HasObservers; use super::HasObservers;
@ -80,6 +79,7 @@ pub struct StdCommandConfigurator {
debug_child: bool, debug_child: bool,
has_stdout_observer: bool, has_stdout_observer: bool,
has_stderr_observer: bool, has_stderr_observer: bool,
timeout: Duration,
/// true: input gets delivered via stdink /// true: input gets delivered via stdink
input_location: InputLocation, input_location: InputLocation,
/// The Command to execute /// The Command to execute
@ -153,6 +153,10 @@ impl CommandConfigurator for StdCommandConfigurator {
} }
} }
} }
fn exec_timeout(&self) -> Duration {
self.timeout
}
} }
/// A `CommandExecutor` is a wrapper around [`std::process::Command`] to execute a target as a child process. /// A `CommandExecutor` is a wrapper around [`std::process::Command`] to execute a target as a child process.
@ -219,6 +223,7 @@ where
pub fn from_cmd_with_file<P>( pub fn from_cmd_with_file<P>(
cmd: &Command, cmd: &Command,
debug_child: bool, debug_child: bool,
timeout: Duration,
observers: OT, observers: OT,
path: P, path: P,
) -> Result<Self, Error> ) -> Result<Self, Error>
@ -251,6 +256,7 @@ where
debug_child, debug_child,
has_stdout_observer, has_stdout_observer,
has_stderr_observer, has_stderr_observer,
timeout,
}, },
phantom: PhantomData, phantom: PhantomData,
}) })
@ -265,6 +271,7 @@ where
args: IT, args: IT,
observers: OT, observers: OT,
debug_child: bool, debug_child: bool,
timeout: Duration,
) -> Result<Self, Error> ) -> Result<Self, Error>
where where
IT: IntoIterator<Item = O>, IT: IntoIterator<Item = O>,
@ -273,6 +280,7 @@ where
let mut atat_at = None; let mut atat_at = None;
let mut builder = CommandExecutorBuilder::new(); let mut builder = CommandExecutorBuilder::new();
builder.debug_child(debug_child); builder.debug_child(debug_child);
builder.timeout(timeout);
let afl_delim = OsStr::new("@@"); let afl_delim = OsStr::new("@@");
for (pos, arg) in args.into_iter().enumerate() { for (pos, arg) in args.into_iter().enumerate() {
@ -325,7 +333,7 @@ where
let mut child = self.configurer.spawn_child(input)?; let mut child = self.configurer.spawn_child(input)?;
let res = match child let res = match child
.wait_timeout(Duration::from_secs(5)) .wait_timeout(self.configurer.exec_timeout())
.expect("waiting on child failed") .expect("waiting on child failed")
.map(|status| status.signal()) .map(|status| status.signal())
{ {
@ -405,6 +413,7 @@ pub struct CommandExecutorBuilder {
input_location: InputLocation, input_location: InputLocation,
cwd: Option<PathBuf>, cwd: Option<PathBuf>,
envs: Vec<(OsString, OsString)>, envs: Vec<(OsString, OsString)>,
timeout: Duration,
} }
impl Default for CommandExecutorBuilder { impl Default for CommandExecutorBuilder {
@ -423,6 +432,7 @@ impl CommandExecutorBuilder {
input_location: InputLocation::StdIn, input_location: InputLocation::StdIn,
cwd: None, cwd: None,
envs: vec![], envs: vec![],
timeout: Duration::from_secs(5),
debug_child: false, debug_child: false,
} }
} }
@ -541,6 +551,12 @@ impl CommandExecutorBuilder {
self self
} }
/// Sets the execution timeout duration.
pub fn timeout(&mut self, timeout: Duration) -> &mut CommandExecutorBuilder {
self.timeout = timeout;
self
}
/// Builds the `CommandExecutor` /// Builds the `CommandExecutor`
pub fn build<OT, S>( pub fn build<OT, S>(
&self, &self,
@ -591,6 +607,7 @@ impl CommandExecutorBuilder {
has_stdout_observer: observers.observes_stdout(), has_stdout_observer: observers.observes_stdout(),
has_stderr_observer: observers.observes_stderr(), has_stderr_observer: observers.observes_stderr(),
input_location: self.input_location.clone(), input_location: self.input_location.clone(),
timeout: self.timeout,
command, command,
}; };
Ok(configurator.into_executor::<OT, S>(observers)) Ok(configurator.into_executor::<OT, S>(observers))
@ -601,7 +618,7 @@ impl CommandExecutorBuilder {
/// # Example /// # Example
#[cfg_attr(all(feature = "std", unix), doc = " ```")] #[cfg_attr(all(feature = "std", unix), doc = " ```")]
#[cfg_attr(not(all(feature = "std", unix)), doc = " ```ignore")] #[cfg_attr(not(all(feature = "std", unix)), doc = " ```ignore")]
/// use std::{io::Write, process::{Stdio, Command, Child}}; /// use std::{io::Write, process::{Stdio, Command, Child}, time::Duration};
/// use libafl::{Error, bolts::AsSlice, inputs::{HasTargetBytes, Input, UsesInput}, executors::{Executor, command::CommandConfigurator}, state::UsesState}; /// use libafl::{Error, bolts::AsSlice, inputs::{HasTargetBytes, Input, UsesInput}, executors::{Executor, command::CommandConfigurator}, state::UsesState};
/// #[derive(Debug)] /// #[derive(Debug)]
/// struct MyExecutor; /// struct MyExecutor;
@ -622,6 +639,10 @@ impl CommandExecutorBuilder {
/// stdin.write_all(input.target_bytes().as_slice())?; /// stdin.write_all(input.target_bytes().as_slice())?;
/// Ok(child) /// Ok(child)
/// } /// }
///
/// fn exec_timeout(&self) -> Duration {
/// Duration::from_secs(5)
/// }
/// } /// }
/// ///
/// fn make_executor<EM, Z>() -> impl Executor<EM, Z> /// fn make_executor<EM, Z>() -> impl Executor<EM, Z>
@ -642,6 +663,9 @@ pub trait CommandConfigurator: Sized + Debug {
where where
I: Input + HasTargetBytes; I: Input + HasTargetBytes;
/// Provides timeout duration for execution of the child process.
fn exec_timeout(&self) -> Duration;
/// Create an `Executor` from this `CommandConfigurator`. /// Create an `Executor` from this `CommandConfigurator`.
fn into_executor<OT, S>(self, observers: OT) -> CommandExecutor<OT, S, Self> fn into_executor<OT, S>(self, observers: OT) -> CommandExecutor<OT, S, Self>
where where
@ -699,14 +723,19 @@ mod tests {
#[cfg_attr(miri, ignore)] #[cfg_attr(miri, ignore)]
fn test_parse_afl_cmdline() { fn test_parse_afl_cmdline() {
use alloc::string::ToString; use alloc::string::ToString;
use core::time::Duration;
let mut mgr = SimpleEventManager::new(SimpleMonitor::new(|status| { let mut mgr = SimpleEventManager::new(SimpleMonitor::new(|status| {
log::info!("{status}"); log::info!("{status}");
})); }));
let mut executor = let mut executor = CommandExecutor::parse_afl_cmdline(
CommandExecutor::parse_afl_cmdline(["file".to_string(), "@@".to_string()], (), true) ["file".to_string(), "@@".to_string()],
.unwrap(); (),
true,
Duration::from_secs(5),
)
.unwrap();
executor executor
.run_target( .run_target(
&mut NopFuzzer::new(), &mut NopFuzzer::new(),