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

Create temp working directory for each libfuzzer invocation #3603

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
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
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.

3 changes: 1 addition & 2 deletions src/agent/coverage/fuzz/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ cargo-fuzz = true

[dependencies]
libfuzzer-sys = "0.4"
tempfile = "3.7"
tempfile = "3.8"
debuggable-module = { path = "../../debuggable-module" }


Expand All @@ -34,4 +34,3 @@ name = "fuzz_target_allowlist_parse"
path = "fuzz_targets/fuzz_target_allowlist_parse.rs"
test = false
doc = false

1 change: 1 addition & 0 deletions src/agent/onefuzz-agent/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ azure_storage = { version = "0.15", default-features = false, features = [
azure_storage_blobs = { version = "0.15", default-features = false, features = [
"enable_reqwest",
] }
tempfile = "3.8"


[target.'cfg(target_family = "unix")'.dependencies]
Expand Down
3 changes: 2 additions & 1 deletion src/agent/onefuzz-agent/src/validations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ async fn run_setup(setup_folder: impl AsRef<Path>) -> Result<()> {
}

async fn get_logs(config: ValidationConfig) -> Result<()> {
let tmp_working_dir = tempfile::tempdir()?;
let setup_folder = config
.setup_folder
.clone()
Expand All @@ -121,7 +122,7 @@ async fn get_logs(config: ValidationConfig) -> Result<()> {
},
);

let cmd = libfuzzer.build_std_command(None, None, None, None, None)?;
let cmd = libfuzzer.build_std_command(tmp_working_dir.path(), None, None, None, None, None)?;
print_logs(cmd)?;
Ok(())
}
Expand Down
18 changes: 12 additions & 6 deletions src/agent/onefuzz-task/src/tasks/fuzz/libfuzzer/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@ where
worker_id: usize,
stats_sender: Option<&StatsSender>,
) -> Result<()> {
let tmp_working_dir = self.create_local_temp_dir().await?;
let crash_dir = self.create_local_temp_dir().await?;
let run_id = Uuid::new_v4();

Expand All @@ -272,7 +273,12 @@ where
info!("config is: {:?}", self.config);

let fuzzer = L::from_config(&self.config).await?;
let mut running = fuzzer.fuzz(crash_dir.path(), local_inputs, &inputs)?;
let mut running = fuzzer.fuzz(
tmp_working_dir.path(),
crash_dir.path(),
local_inputs,
&inputs,
)?;

info!("child is: {:?}", running);

Expand Down Expand Up @@ -376,22 +382,22 @@ where
// note that collecting the dumps must be enabled by the template
#[cfg(target_os = "linux")]
if let Some(pid) = pid {
// expect crash dump to exist in CWD
let filename = format!("core.{pid}");
// expect crash dump to exist in fuzzer (temp) working dir
let filename = tmp_working_dir.path().join(format!("core.{pid}"));
let dest_filename = dump_file_name.as_deref().unwrap_or(OsStr::new(&filename));
let dest_path = crashdumps.local_path.join(dest_filename);
match tokio::fs::rename(&filename, &dest_path).await {
Ok(()) => {
info!(
"moved crash dump {} to output directory: {}",
filename,
filename.display(),
dest_path.display()
);
}
Err(e) => {
if e.kind() == std::io::ErrorKind::NotFound {
// okay, no crash dump found
info!("no crash dump found with name: {}", filename);
info!("no crash dump found with name: {}", filename.display());
} else {
return Err(e).context("moving crash dump to output directory");
}
Expand All @@ -406,7 +412,7 @@ where
{
let dumpfile_extension = Some(std::ffi::OsStr::new("dmp"));

let mut working_dir = tokio::fs::read_dir(".").await?;
let mut working_dir = tokio::fs::read_dir(tmp_working_dir.path()).await?;
let mut found_dump = false;
while let Some(next) = working_dir.next_entry().await? {
if next.path().extension() == dumpfile_extension {
Expand Down
31 changes: 28 additions & 3 deletions src/agent/onefuzz/src/libfuzzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,15 @@ impl LibFuzzer {
// Build an async `Command`.
fn build_command(
&self,
working_dir: &Path,
fault_dir: Option<&Path>,
corpus_dir: Option<&Path>,
extra_corpus_dirs: Option<&[&Path]>,
extra_args: Option<&[&OsStr]>,
custom_arg_filter: Option<&dyn Fn(String) -> Option<String>>,
) -> Result<Command> {
let std_cmd = self.build_std_command(
working_dir,
fault_dir,
corpus_dir,
extra_corpus_dirs,
Expand All @@ -96,13 +98,20 @@ impl LibFuzzer {
// Build a non-async `Command`.
pub fn build_std_command(
&self,
working_dir: &Path,
fault_dir: Option<&Path>,
corpus_dir: Option<&Path>,
extra_corpus_dirs: Option<&[&Path]>,
extra_args: Option<&[&OsStr]>,
custom_arg_filter: Option<&dyn Fn(String) -> Option<String>>,
) -> Result<std::process::Command> {
let mut cmd = std::process::Command::new(&self.exe);

// subprocess is isolated in its own working directory
// this is to prevent collisions between multiple libfuzzers
// (for example, crash dumps are generated into working directory on Windows)
cmd.current_dir(working_dir);

cmd.env(PATH, get_path_with_directory(PATH, &self.setup_dir)?)
.env_remove("RUST_LOG")
.stdin(Stdio::null())
Expand Down Expand Up @@ -250,13 +259,16 @@ impl LibFuzzer {
// Verify that the libfuzzer exits with a zero return code with a known
// good input, which libfuzzer works as we expect.
async fn check_input(&self, input: &Path) -> Result<()> {
let absolute_input = dunce::canonicalize(input)?;
let tmp_working_dir = tempdir()?;
let mut cmd = self.build_command(
tmp_working_dir.path(),
None,
None,
None,
// Custom args for this run: supply the required input. In this mode,
// LibFuzzer will only execute one run of fuzzing unless overridden
Some(&[input.as_ref()]),
Some(&[absolute_input.as_ref()]),
// Filter out any argument starting with `-runs=` from the custom
// target options, if supplied, so that it doesn't make more than
// one run happen:
Expand Down Expand Up @@ -294,7 +306,15 @@ impl LibFuzzer {
/// 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, Some(&["-help=1".as_ref()]), None)?;
let tmp_working_dir = tempdir()?;
let mut cmd = self.build_command(
tmp_working_dir.path(),
None,
None,
None,
Some(&["-help=1".as_ref()]),
None,
)?;

let result = cmd
.spawn()
Expand Down Expand Up @@ -326,7 +346,8 @@ impl LibFuzzer {
}

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

#[cfg(target_os = "linux")]
let blocking = move || dynamic_library::linux::find_missing(cmd);
Expand All @@ -343,6 +364,7 @@ impl LibFuzzer {

pub fn fuzz(
&self,
working_dir: &Path,
fault_dir: impl AsRef<Path>,
corpus_dir: impl AsRef<Path>,
extra_corpus_dirs: &[impl AsRef<Path>],
Expand All @@ -357,6 +379,7 @@ impl LibFuzzer {
let artifact_prefix = artifact_prefix(fault_dir.as_ref());

let mut cmd = self.build_command(
working_dir,
Some(fault_dir.as_ref()),
Some(corpus_dir.as_ref()),
Some(&extra_corpus_dirs),
Expand Down Expand Up @@ -406,8 +429,10 @@ impl LibFuzzer {
corpus_dir: impl AsRef<Path>,
extra_corpus_dirs: &[impl AsRef<Path>],
) -> Result<LibFuzzerMergeOutput> {
let tmp_working_dir = tempdir()?;
let extra_corpus_dirs: Vec<&Path> = extra_corpus_dirs.iter().map(|x| x.as_ref()).collect();
let mut cmd = self.build_command(
tmp_working_dir.path(),
None,
Some(corpus_dir.as_ref()),
Some(&extra_corpus_dirs),
Expand Down
Loading