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(),