Skip to content

Commit

Permalink
Adding ExitCodeObserver and ExitStatusObserver
Browse files Browse the repository at this point in the history
  • Loading branch information
riesentoaster committed May 3, 2024
1 parent 2f7c19e commit e8a1a75
Show file tree
Hide file tree
Showing 4 changed files with 221 additions and 18 deletions.
51 changes: 33 additions & 18 deletions libafl/src/executors/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -334,24 +334,9 @@ where

let mut child = self.configurer.spawn_child(input)?;

let res = match child
let res = child
.wait_timeout(self.configurer.exec_timeout())
.expect("waiting on child failed")
.map(|status| status.signal())
{
// for reference: https://www.man7.org/linux/man-pages/man7/signal.7.html
Some(Some(9)) => Ok(ExitKind::Oom),
Some(Some(_)) => Ok(ExitKind::Crash),
Some(None) => Ok(ExitKind::Ok),
None => {
// if this fails, there is not much we can do. let's hope it failed because the process finished
// in the meantime.
drop(child.kill());
// finally, try to wait to properly clean up system resources.
drop(child.wait());
Ok(ExitKind::Timeout)
}
};
.expect("waiting on child failed");

if self.observers.observes_stderr() {
let mut stderr = Vec::new();
Expand All @@ -362,6 +347,7 @@ where
})?.read_to_end(&mut stderr)?;
self.observers.observe_stderr(&stderr);
}

if self.observers.observes_stdout() {
let mut stdout = Vec::new();
child.stdout.as_mut().ok_or_else(|| {
Expand All @@ -372,7 +358,36 @@ where
self.observers.observe_stdout(&stdout);
}

res
if self.observers.observes_exit_code() {
if let Some(r) = res {
if let Some(exit_code) = r.code() {
self.observers.observe_exit_code(exit_code);
}
}
}

if self.observers.observes_exit_signal() {
if let Some(r) = res {
if let Some(exit_signal) = r.signal() {
self.observers.observe_exit_signal(exit_signal);
}
}
}

match res.map(|status| status.signal()) {
// for reference: https://www.man7.org/linux/man-pages/man7/signal.7.html
Some(Some(9)) => Ok(ExitKind::Oom),
Some(Some(_)) => Ok(ExitKind::Crash),
Some(None) => Ok(ExitKind::Ok),
None => {
// if this fails, there is not much we can do. let's hope it failed because the process finished
// in the meantime.
drop(child.kill());
// finally, try to wait to properly clean up system resources.
drop(child.wait());
Ok(ExitKind::Timeout)
}
}
}
}

Expand Down
25 changes: 25 additions & 0 deletions libafl/src/executors/differential.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,17 @@ where
fn observes_stderr(&self) -> bool {
self.primary.as_ref().observes_stderr() || self.secondary.as_ref().observes_stderr()
}
/// Returns true if an exit code observer was added to the list
#[inline]
fn observes_exit_code(&self) -> bool {
self.primary.as_ref().observes_exit_code() || self.secondary.as_ref().observes_exit_code()
}
/// Returns true if an exit signal observer was added to the list
#[inline]
fn observes_exit_signal(&self) -> bool {
self.primary.as_ref().observes_exit_signal()
|| self.secondary.as_ref().observes_exit_signal()
}

/// Runs `observe_stdout` for all stdout observers in the list
fn observe_stdout(&mut self, stdout: &[u8]) {
Expand All @@ -177,6 +188,20 @@ where
fn observe_stderr(&mut self, stderr: &[u8]) {
self.primary.as_mut().observe_stderr(stderr);
self.secondary.as_mut().observe_stderr(stderr);
self.primary.as_mut().observe_stderr(stderr);
self.secondary.as_mut().observe_stderr(stderr);
}

/// Runs `observe_exit_code` for all exit code observers in the list
fn observe_exit_code(&mut self, exit_code: i32) {
self.primary.as_mut().observe_exit_code(exit_code);
self.secondary.as_mut().observe_exit_code(exit_code);
}

/// Runs `observe_exit_signal` for all exit signal observers in the list
fn observe_exit_signal(&mut self, exit_signal: i32) {
self.primary.as_mut().observe_exit_signal(exit_signal);
self.secondary.as_mut().observe_exit_signal(exit_signal);
}
}

Expand Down
77 changes: 77 additions & 0 deletions libafl/src/observers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,16 @@ where
fn observes_stderr(&self) -> bool {
false
}
/// If this observer observes exit codes
#[inline]
fn observes_exit_code(&self) -> bool {
false
}
/// If this observer observes exit signals
#[inline]
fn observes_exit_signal(&self) -> bool {
false
}

/// React to new `stdout`
/// To use this, always return `true` from `observes_stdout`
Expand All @@ -107,6 +117,17 @@ where
#[inline]
#[allow(unused_variables)]
fn observe_stderr(&mut self, stderr: &[u8]) {}

/// React to new exit code
/// To use this, always return `true` from `observes_exit_code`
#[inline]
#[allow(unused_variables)]
fn observe_exit_code(&mut self, exit_code: i32) {}
/// React to new exit signal
/// To use this, always return `true` from `observes_exit_signal`
#[inline]
#[allow(unused_variables)]
fn observe_exit_signal(&mut self, exit_signal: i32) {}
}

/// Defines the observer type shared across traits of the type.
Expand Down Expand Up @@ -147,11 +168,19 @@ where
fn observes_stdout(&self) -> bool;
/// Returns true if a `stderr` observer was added to the list
fn observes_stderr(&self) -> bool;
/// Returns true if an exit code observer was added to the list
fn observes_exit_code(&self) -> bool;
/// Returns true if an exit signal observer was added to the list
fn observes_exit_signal(&self) -> bool;

/// Runs `observe_stdout` for all stdout observers in the list
fn observe_stdout(&mut self, stdout: &[u8]);
/// Runs `observe_stderr` for all stderr observers in the list
fn observe_stderr(&mut self, stderr: &[u8]);
/// Runs `observe_exit_code` for all exit code observers in the list
fn observe_exit_code(&mut self, exit_code: i32);
/// Runs `observe_exit_signal` for all exit signal observers in the list
fn observe_exit_signal(&mut self, exit_signal: i32);
}

impl<S> ObserversTuple<S> for ()
Expand Down Expand Up @@ -196,6 +225,18 @@ where
false
}

/// Returns true if an exit code observer was added to the list
#[inline]
fn observes_exit_code(&self) -> bool {
false
}

/// Returns true if an exit signal observer was added to the list
#[inline]
fn observes_exit_signal(&self) -> bool {
false
}

/// Runs `observe_stdout` for all stdout observers in the list
#[inline]
#[allow(unused_variables)]
Expand All @@ -205,6 +246,16 @@ where
#[inline]
#[allow(unused_variables)]
fn observe_stderr(&mut self, stderr: &[u8]) {}

/// Runs `observe_exit_code` for all exit code observers in the list
#[inline]
#[allow(unused_variables)]
fn observe_exit_code(&mut self, exit_code: i32) {}

/// Runs `observe_exit_signal` for all exit signal observers in the list
#[inline]
#[allow(unused_variables)]
fn observe_exit_signal(&mut self, exit_signal: i32) {}
}

impl<Head, Tail, S> ObserversTuple<S> for (Head, Tail)
Expand Down Expand Up @@ -255,6 +306,18 @@ where
self.0.observes_stderr() || self.1.observes_stderr()
}

/// Returns true if an exit code observer was added to the list
#[inline]
fn observes_exit_code(&self) -> bool {
self.0.observes_exit_code() || self.1.observes_exit_code()
}

/// Returns true if an exit signal observer was added to the list
#[inline]
fn observes_exit_signal(&self) -> bool {
self.0.observes_exit_signal() || self.1.observes_exit_signal()
}

/// Runs `observe_stdout` for all stdout observers in the list
#[inline]
fn observe_stdout(&mut self, stdout: &[u8]) {
Expand All @@ -268,6 +331,20 @@ where
self.0.observe_stderr(stderr);
self.1.observe_stderr(stderr);
}

/// Runs `observe_exit_code` for all exit code observers in the list
#[inline]
fn observe_exit_code(&mut self, exit_code: i32) {
self.0.observe_exit_code(exit_code);
self.1.observe_exit_code(exit_code);
}

/// Runs `observe_exit_signal` for all exit signal observers in the list
#[inline]
fn observe_exit_signal(&mut self, exit_signal: i32) {
self.0.observe_exit_signal(exit_signal);
self.1.observe_exit_signal(exit_signal);
}
}

/// A trait for [`Observer`]`s` with a hash field
Expand Down
86 changes: 86 additions & 0 deletions libafl/src/observers/stdio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,89 @@ impl Named for StdErrObserver {
&self.name
}
}

/// An observer that captures the exit code of a target.
/// Only works for supported executors.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct ExitCodeObserver {
/// The name of the observer.
pub name: Cow<'static, str>,
/// The exit code of the target during its last execution.
pub exit_code: Option<i32>,
}

/// An observer that captures exit signal of a target.
impl ExitCodeObserver {
/// Create a new [`ExitCodeObserver`] with the given name.
#[must_use]
pub fn new(name: &'static str) -> Self {
Self {
name: Cow::from(name),
exit_code: None,
}
}
}

impl<S> Observer<S> for ExitCodeObserver
where
S: UsesInput,
{
#[inline]
fn observes_exit_code(&self) -> bool {
true
}

/// React to new exit code
fn observe_exit_code(&mut self, exit_code: i32) {
self.exit_code = Some(exit_code);
}
}

impl Named for ExitCodeObserver {
fn name(&self) -> &Cow<'static, str> {
&self.name
}
}

/// An observer that captures the exit code of a target.
/// Only works for supported executors.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct ExitSignalObserver {
/// The name of the observer.
pub name: Cow<'static, str>,
/// The exit signal of the target during its last execution.
pub exit_signal: Option<i32>,
}

/// An observer that captures the exit signal of a target.
impl ExitSignalObserver {
/// Create a new [`ExitSignalObserver`] with the given name.
#[must_use]
pub fn new(name: &'static str) -> Self {
Self {
name: Cow::from(name),
exit_signal: None,
}
}
}

impl<S> Observer<S> for ExitSignalObserver
where
S: UsesInput,
{
#[inline]
fn observes_exit_signal(&self) -> bool {
true
}

/// React to new exit signal
fn observe_exit_signal(&mut self, exit_signal: i32) {
self.exit_signal = Some(exit_signal);
}
}

impl Named for ExitSignalObserver {
fn name(&self) -> &Cow<'static, str> {
&self.name
}
}

0 comments on commit e8a1a75

Please sign in to comment.