Skip to content
This repository has been archived by the owner on Nov 1, 2023. It is now read-only.

Check for missing shared libs on failed LibFuzzer -help check #1812

Merged
merged 9 commits into from
Apr 21, 2022
Merged
Show file tree
Hide file tree
Changes from 4 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
1 change: 1 addition & 0 deletions src/agent/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src/agent/onefuzz/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ async-trait = "0.1"
base64 = "0.13"
bytes = "1.1"
dunce = "1.0"
dynamic-library = { path = "../dynamic-library" }
futures = "0.3"
futures-util = "0.3"
hex = "0.4"
Expand Down
63 changes: 59 additions & 4 deletions src/agent/onefuzz/src/libfuzzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use std::{
};
use tempfile::tempdir;
use tokio::process::{Child, Command};
use tokio::task::spawn_blocking;

const DEFAULT_MAX_TOTAL_SECONDS: i32 = 10 * 60;

Expand Down Expand Up @@ -56,15 +57,35 @@ impl<'a> LibFuzzer<'a> {
}
}

// Build an async `Command`.
async fn build_command(
&self,
fault_dir: Option<&Path>,
corpus_dir: Option<&Path>,
extra_corpus_dirs: Option<&[&Path]>,
) -> Result<Command> {
let mut cmd = Command::new(&self.exe);
cmd.kill_on_drop(true)
.env(PATH, get_path_with_directory(PATH, &self.setup_dir)?)
let std_cmd = self
.build_std_command(fault_dir, corpus_dir, extra_corpus_dirs)
.await?;

// Make async.
let mut cmd = Command::from(std_cmd);

// Terminate the process if the `Child` handle is dropped.
cmd.kill_on_drop(true);

Ok(cmd)
}

// Build a non-async `Command`.
async fn build_std_command(
&self,
fault_dir: Option<&Path>,
corpus_dir: Option<&Path>,
extra_corpus_dirs: Option<&[&Path]>,
) -> Result<std::process::Command> {
let mut cmd = std::process::Command::new(&self.exe);
cmd.env(PATH, get_path_with_directory(PATH, &self.setup_dir)?)
.env_remove("RUST_LOG")
.stdin(Stdio::null())
.stdout(Stdio::piped())
Expand Down Expand Up @@ -199,6 +220,9 @@ impl<'a> LibFuzzer<'a> {
Ok(())
}

/// Invoke `{target_exe} -help=1`. If this succeeds, then the dynamic linker is at
/// least able to satisfy the fuzzer's shared library dependencies. User-authored
/// dynamic loading may still fail later on, e.g. in `LLVMFuzzerInitialize()`.
async fn check_help(&self) -> Result<()> {
let mut cmd = self.build_command(None, None, None).await?;
cmd.arg("-help=1");
Expand All @@ -209,12 +233,43 @@ impl<'a> LibFuzzer<'a> {
.wait_with_output()
.await
.with_context(|| format_err!("libfuzzer failed to run: {}", self.exe.display()))?;

if !result.status.success() {
bail!("fuzzer does not respond to '-help=1'. output:{:?}", result);
// To provide user-actionable errors, try to identify any missing shared libraries.
match self.find_missing_libraries().await {
Ok(missing) => {
if missing.is_empty() {
bail!("fuzzer does not respond to '-help=1'. no missing shared libraries detected. output: {:?}", result);
} else {
let missing = missing.join(", ");

bail!("fuzzer does not respond to '-help=1'. missing shared libraries: {}. output: {:?}", missing, result);
}
}
Err(err) => {
bail!("fuzzer does not respond to '-help=1'. additional error while checking for missing shared libraries: {}. output: {:?}", err, result);
}
}
}

Ok(())
}

async fn find_missing_libraries(&self) -> Result<Vec<String>> {
let cmd = self.build_std_command(None, None, None).await?;

#[cfg(target_os = "linux")]
let blocking = move || dynamic_library::linux::find_missing(cmd);

#[cfg(target_os = "windows")]
let blocking = move || dynamic_library::windows::find_missing(cmd);

let missing = spawn_blocking(blocking).await??;
let missing = missing.into_iter().map(|m| m.name).collect();

Ok(missing)
}

pub async fn fuzz(
&self,
fault_dir: impl AsRef<Path>,
Expand Down