Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow configuring timeout for CommandExecutor #1269

Merged
merged 3 commits into from
May 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions fuzzers/backtrace_baby_fuzzers/command_executor/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::{
io::Write,
path::PathBuf,
process::{Child, Command, Stdio},
time::Duration,
};

use libafl::{
Expand Down Expand Up @@ -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));
Expand Down
5 changes: 5 additions & 0 deletions fuzzers/libfuzzer_stb_image_concolic/fuzzer/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use std::{
env,
path::PathBuf,
process::{Child, Command, Stdio},
time::Duration,
};

use clap::{self, Parser};
Expand Down Expand Up @@ -251,4 +252,8 @@ impl CommandConfigurator for MyCommandConfigurator {
.spawn()
.expect("failed to start process"))
}

fn exec_timeout(&self) -> Duration {
Duration::from_secs(5)
}
}
43 changes: 36 additions & 7 deletions libafl/src/executors/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -219,6 +223,7 @@ where
pub fn from_cmd_with_file<P>(
cmd: &Command,
debug_child: bool,
timeout: Duration,
observers: OT,
path: P,
) -> Result<Self, Error>
Expand Down Expand Up @@ -251,6 +256,7 @@ where
debug_child,
has_stdout_observer,
has_stderr_observer,
timeout,
},
phantom: PhantomData,
})
Expand All @@ -265,6 +271,7 @@ where
args: IT,
observers: OT,
debug_child: bool,
timeout: Duration,
) -> Result<Self, Error>
where
IT: IntoIterator<Item = O>,
Expand All @@ -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() {
Expand Down Expand Up @@ -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())
{
Expand Down Expand Up @@ -405,6 +413,7 @@ pub struct CommandExecutorBuilder {
input_location: InputLocation,
cwd: Option<PathBuf>,
envs: Vec<(OsString, OsString)>,
timeout: Duration,
}

impl Default for CommandExecutorBuilder {
Expand All @@ -423,6 +432,7 @@ impl CommandExecutorBuilder {
input_location: InputLocation::StdIn,
cwd: None,
envs: vec![],
timeout: Duration::from_secs(5),
debug_child: false,
}
}
Expand Down Expand Up @@ -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<OT, S>(
&self,
Expand Down Expand Up @@ -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::<OT, S>(observers))
Expand All @@ -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;
Expand All @@ -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<EM, Z>() -> impl Executor<EM, Z>
Expand All @@ -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<OT, S>(self, observers: OT) -> CommandExecutor<OT, S, Self>
where
Expand Down Expand Up @@ -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(),
Expand Down