From 6d2284d8b9c82d6a51ed55831c0286e3e9df212e Mon Sep 17 00:00:00 2001 From: Arpan Kapoor Date: Mon, 15 May 2023 16:29:06 +0530 Subject: [PATCH] Allow configuring timeout for CommandExecutor (#1269) * Allow configuring timeout for CommandExecutor * import Duration on windows as well * fix example fuzzers --- .../command_executor/src/main.rs | 5 +++ .../fuzzer/src/main.rs | 5 +++ libafl/src/executors/command.rs | 43 ++++++++++++++++--- 3 files changed, 46 insertions(+), 7 deletions(-) diff --git a/fuzzers/backtrace_baby_fuzzers/command_executor/src/main.rs b/fuzzers/backtrace_baby_fuzzers/command_executor/src/main.rs index 6f8c4fdbed..78817c420e 100644 --- a/fuzzers/backtrace_baby_fuzzers/command_executor/src/main.rs +++ b/fuzzers/backtrace_baby_fuzzers/command_executor/src/main.rs @@ -4,6 +4,7 @@ use std::{ io::Write, path::PathBuf, process::{Child, Command, Stdio}, + time::Duration, }; use libafl::{ @@ -103,6 +104,10 @@ pub fn main() { stdin.write_all(input.target_bytes().as_slice())?; Ok(child) } + + fn exec_timeout(&self) -> Duration { + Duration::from_secs(5) + } } let mut executor = MyExecutor { shmem_id }.into_executor(tuple_list!(observer, bt_observer)); diff --git a/fuzzers/libfuzzer_stb_image_concolic/fuzzer/src/main.rs b/fuzzers/libfuzzer_stb_image_concolic/fuzzer/src/main.rs index 45de3ded48..ac514f3c54 100644 --- a/fuzzers/libfuzzer_stb_image_concolic/fuzzer/src/main.rs +++ b/fuzzers/libfuzzer_stb_image_concolic/fuzzer/src/main.rs @@ -8,6 +8,7 @@ use std::{ env, path::PathBuf, process::{Child, Command, Stdio}, + time::Duration, }; use clap::{self, Parser}; @@ -251,4 +252,8 @@ impl CommandConfigurator for MyCommandConfigurator { .spawn() .expect("failed to start process")) } + + fn exec_timeout(&self) -> Duration { + Duration::from_secs(5) + } } diff --git a/libafl/src/executors/command.rs b/libafl/src/executors/command.rs index e4d7694ffa..a92022f63f 100644 --- a/libafl/src/executors/command.rs +++ b/libafl/src/executors/command.rs @@ -8,13 +8,12 @@ use core::{ use std::os::unix::ffi::OsStrExt; #[cfg(feature = "std")] use std::process::Child; -#[cfg(all(feature = "std", unix))] -use std::time::Duration; use std::{ ffi::{OsStr, OsString}, io::{Read, Write}, path::{Path, PathBuf}, process::{Command, Stdio}, + time::Duration, }; use super::HasObservers; @@ -80,6 +79,7 @@ pub struct StdCommandConfigurator { debug_child: bool, has_stdout_observer: bool, has_stderr_observer: bool, + timeout: Duration, /// true: input gets delivered via stdink input_location: InputLocation, /// 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. @@ -219,6 +223,7 @@ where pub fn from_cmd_with_file

( cmd: &Command, debug_child: bool, + timeout: Duration, observers: OT, path: P, ) -> Result @@ -251,6 +256,7 @@ where debug_child, has_stdout_observer, has_stderr_observer, + timeout, }, phantom: PhantomData, }) @@ -265,6 +271,7 @@ where args: IT, observers: OT, debug_child: bool, + timeout: Duration, ) -> Result where IT: IntoIterator, @@ -273,6 +280,7 @@ where let mut atat_at = None; let mut builder = CommandExecutorBuilder::new(); builder.debug_child(debug_child); + builder.timeout(timeout); let afl_delim = OsStr::new("@@"); for (pos, arg) in args.into_iter().enumerate() { @@ -325,7 +333,7 @@ where let mut child = self.configurer.spawn_child(input)?; let res = match child - .wait_timeout(Duration::from_secs(5)) + .wait_timeout(self.configurer.exec_timeout()) .expect("waiting on child failed") .map(|status| status.signal()) { @@ -405,6 +413,7 @@ pub struct CommandExecutorBuilder { input_location: InputLocation, cwd: Option, envs: Vec<(OsString, OsString)>, + timeout: Duration, } impl Default for CommandExecutorBuilder { @@ -423,6 +432,7 @@ impl CommandExecutorBuilder { input_location: InputLocation::StdIn, cwd: None, envs: vec![], + timeout: Duration::from_secs(5), debug_child: false, } } @@ -541,6 +551,12 @@ impl CommandExecutorBuilder { self } + /// Sets the execution timeout duration. + pub fn timeout(&mut self, timeout: Duration) -> &mut CommandExecutorBuilder { + self.timeout = timeout; + self + } + /// Builds the `CommandExecutor` pub fn build( &self, @@ -591,6 +607,7 @@ impl CommandExecutorBuilder { has_stdout_observer: observers.observes_stdout(), has_stderr_observer: observers.observes_stderr(), input_location: self.input_location.clone(), + timeout: self.timeout, command, }; Ok(configurator.into_executor::(observers)) @@ -601,7 +618,7 @@ impl CommandExecutorBuilder { /// # Example #[cfg_attr(all(feature = "std", unix), doc = " ```")] #[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}; /// #[derive(Debug)] /// struct MyExecutor; @@ -622,6 +639,10 @@ impl CommandExecutorBuilder { /// stdin.write_all(input.target_bytes().as_slice())?; /// Ok(child) /// } +/// +/// fn exec_timeout(&self) -> Duration { +/// Duration::from_secs(5) +/// } /// } /// /// fn make_executor() -> impl Executor @@ -642,6 +663,9 @@ pub trait CommandConfigurator: Sized + Debug { where I: Input + HasTargetBytes; + /// Provides timeout duration for execution of the child process. + fn exec_timeout(&self) -> Duration; + /// Create an `Executor` from this `CommandConfigurator`. fn into_executor(self, observers: OT) -> CommandExecutor where @@ -699,14 +723,19 @@ mod tests { #[cfg_attr(miri, ignore)] fn test_parse_afl_cmdline() { use alloc::string::ToString; + use core::time::Duration; let mut mgr = SimpleEventManager::new(SimpleMonitor::new(|status| { log::info!("{status}"); })); - let mut executor = - CommandExecutor::parse_afl_cmdline(["file".to_string(), "@@".to_string()], (), true) - .unwrap(); + let mut executor = CommandExecutor::parse_afl_cmdline( + ["file".to_string(), "@@".to_string()], + (), + true, + Duration::from_secs(5), + ) + .unwrap(); executor .run_target( &mut NopFuzzer::new(),