Skip to content

Commit

Permalink
Merge pull request #79 from srlabs/forking-coverage
Browse files Browse the repository at this point in the history
Add basic forking coverage support
  • Loading branch information
louismerlin authored Oct 30, 2023
2 parents 205a95e + 532a968 commit 4603859
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 71 deletions.
94 changes: 42 additions & 52 deletions Cargo.lock

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

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ cargo_metadata = { version = "0.18.0", optional = true }
clap = { version = "4.4.6", features = ["cargo", "derive", "env"], optional = true }
console = { version = "0.15.7", optional = true }
env_logger = { version = "0.10.0", optional = true }
fork = { version = "0.1.22", optional = true }
glob = { version = "0.3.1", optional = true }
# We use our own fork of honggfuzz to use the tool's latest release
# https://github.com/rust-fuzz/honggfuzz-rs/pull/85
Expand Down Expand Up @@ -49,3 +50,4 @@ cli = [
"time-humanize",
"cargo_metadata",
]
coverage = ["fork", "libc"]
28 changes: 11 additions & 17 deletions src/bin/cargo-ziggy/coverage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@ impl Cover {

// We build the runner with the appropriate flags for coverage
process::Command::new(cargo)
.args(["rustc", "--target-dir=target/coverage"])
.args([
"rustc",
"--target-dir=target/coverage",
"--features=ziggy/coverage",
])
.env("RUSTFLAGS", coverage_rustflags)
.env("RUSTDOCFLAGS", "-Cpanic=unwind")
.env("CARGO_INCREMENTAL", "0")
Expand Down Expand Up @@ -56,22 +60,12 @@ impl Cover {

info!("Corpus directory is {}", shared_corpus.display());

// We run the target against the corpus
shared_corpus.canonicalize()?.read_dir()?.for_each(|item| {
let item = item.unwrap();
let item_path = item.path();

let result = process::Command::new(format!("./target/coverage/debug/{}", &self.target))
.args([item_path.as_os_str()])
.spawn()
.unwrap()
.wait_with_output()
.unwrap();

if !result.status.success() {
eprintln!("Coverage crashed on {}, continuing.", item_path.display())
}
});
let _ = process::Command::new(format!("./target/coverage/debug/{}", &self.target))
.arg(format!("{}", shared_corpus.display()))
.spawn()
.unwrap()
.wait_with_output()
.unwrap();

let source_or_workspace_root = match &self.source {
Some(s) => s.display().to_string(),
Expand Down
54 changes: 52 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
#![doc = include_str!("../README.md")]
#[cfg(feature = "afl")]
pub use afl::fuzz as afl_fuzz;
#[cfg(feature = "coverage")]
pub use fork;
#[cfg(feature = "honggfuzz")]
pub use honggfuzz::fuzz as honggfuzz_fuzz;

// This is our inner harness handler function for the runner and for coverage.
// This is our inner harness handler function for the runner.
// We open the input file and feed the data to the harness closure.
#[doc(hidden)]
#[cfg(not(any(feature = "afl", feature = "honggfuzz")))]
#[cfg(not(any(feature = "afl", feature = "honggfuzz", feature = "coverage")))]
pub fn read_file_and_fuzz<F>(mut closure: F, file: String)
where
F: FnMut(&[u8]),
Expand All @@ -32,6 +34,53 @@ where
};
}

// This is our special coverage harness runner.
// We open the input file and feed the data to the harness closure.
// The difference with the runner is that we catch any kind of panic.
#[cfg(feature = "coverage")]
pub fn read_file_and_fuzz<F>(mut closure: F, file: String)
where
F: FnMut(&[u8]),
{
use std::{fs::File, io::Read, process::exit};
println!("Now running file {file} for coverage");
let mut buffer: Vec<u8> = Vec::new();
match File::open(file) {
Ok(mut f) => {
match f.read_to_end(&mut buffer) {
Ok(_) => {
use crate::fork::{fork, Fork};

match fork() {
Ok(Fork::Parent(child)) => {
println!(
"Continuing execution in parent process, new child has pid: {}",
child
);
unsafe {
let mut status = 0i32;
let _ = libc::waitpid(child, &mut status, 0);
}
println!("Child is done, moving on");
}
Ok(Fork::Child) => {
closure(buffer.as_slice());
exit(0);
}
Err(_) => println!("Fork failed"),
}
}
Err(e) => {
println!("Could not get data from file: {e}");
}
};
}
Err(e) => {
println!("Error opening file: {e}");
}
};
}

// This is our middle harness handler macro for the runner and for coverage.
// We read input files and directories from the command line and run the inner harness `fuzz`.
#[doc(hidden)]
Expand All @@ -56,6 +105,7 @@ macro_rules! read_args_and_fuzz {
for file in files {
$crate::read_file_and_fuzz(|$buf| $body, file);
}
println!("Finished reading all files");
} else {
println!("Could not read metadata for {path}");
}
Expand Down

0 comments on commit 4603859

Please sign in to comment.