Skip to content

Commit

Permalink
Add new mechanism for running inputs
Browse files Browse the repository at this point in the history
  • Loading branch information
louismerlin committed Dec 9, 2024
1 parent f84025e commit 499e42e
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 125 deletions.
10 changes: 0 additions & 10 deletions Cargo.lock

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

2 changes: 0 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ anyhow = { version = "1.0.83", optional = true }
cargo_metadata = { version = "0.18.1", optional = true }
clap = { version = "4.5.4", features = ["cargo", "derive", "env"], optional = true }
console = { version = "0.15.8", optional = true }
fork = { version = "0.1.23", optional = true }
glob = { version = "0.3.1", optional = true }
honggfuzz = { version = "0.5.56", optional = true }
libc = { version = "0.2.153", optional = true }
Expand All @@ -43,7 +42,6 @@ cli = [
"cargo_metadata",
"twox-hash",
]
coverage = ["fork", "libc"]

[lints.clippy]
needless_doctest_main = "allow"
35 changes: 25 additions & 10 deletions src/bin/cargo-ziggy/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,29 +62,44 @@ impl Run {
}
}

let run_args: Vec<String> = self
let input_files: Vec<PathBuf> = self
.inputs
.iter()
.map(|x| {
x.display()
let canonical_name = x
.display()
.to_string()
.replace("{ziggy_output}", &self.ziggy_output.display().to_string())
.replace("{target_name}", &target)
.replace("{target_name}", &target);
// For each directory we read, we get all files in that directory
let path = PathBuf::from(canonical_name);
match path.is_dir() {
true => fs::read_dir(path)
.expect("could not read directory")
.filter_map(|entry| entry.ok())
.map(|entry| entry.path())
.filter(|path| path.is_file())
.collect::<Vec<_>>(),
false => vec![path],
}
})
.flatten()
.collect();

let runner_path = match self.asan {
true => format!("./target/runner/{ASAN_TARGET}/debug/{}", target),
false => format!("./target/runner/debug/{}", target),
};

process::Command::new(runner_path)
.args(run_args)
.env("RUST_BACKTRACE", "full")
.spawn()
.context("⚠️ couldn't spawn the runner process")?
.wait()
.context("⚠️ couldn't wait for the runner process")?;
for file in input_files {
process::Command::new(&runner_path)
.arg(file)
.env("RUST_BACKTRACE", "full")
.spawn()
.context("⚠️ couldn't spawn the runner process")?
.wait()
.context("⚠️ couldn't wait for the runner process")?;
}

Ok(())
}
Expand Down
120 changes: 17 additions & 103 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,116 +1,30 @@
#![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.
// We open the input file and feed the data to the harness closure.
#[doc(hidden)]
#[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]),
{
use std::{fs::File, io::Read};
println!("Now running file {file}");
let mut buffer: Vec<u8> = Vec::new();
match File::open(file) {
Ok(mut f) => {
match f.read_to_end(&mut buffer) {
Ok(_) => {
closure(buffer.as_slice());
}
Err(e) => {
println!("Could not get data from file: {e}");
}
};
}
Err(e) => {
println!("Error opening file: {e}");
}
};
}

// 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)
#[cfg(not(any(feature = "afl", feature = "honggfuzz")))]
pub fn run_file<F>(mut closure: F)
where
F: FnMut(&[u8]),
{
use std::{fs::File, io::Read, process::exit};
println!("Now running file {file} for coverage");
use std::{env, fs::File, io::Read};
let file_name: String = env::args().nth(1).expect("pass in a file name as argument");
println!("Now running {file_name}");
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)]
#[macro_export]
#[cfg(not(any(feature = "afl", feature = "honggfuzz")))]
macro_rules! read_args_and_fuzz {
( |$buf:ident| $body:block ) => {
use std::{env, fs};
let args: Vec<String> = env::args().collect();
for path in &args[1..] {
if let Ok(metadata) = fs::metadata(&path) {
let files = match metadata.is_dir() {
true => fs::read_dir(&path)
.unwrap()
.map(|x| x.unwrap().path())
.filter(|x| x.is_file())
.map(|x| x.to_str().unwrap().to_string())
.collect::<Vec<String>>(),
false => vec![path.to_string()],
};

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}");
}
}
};
let mut file = File::open(file_name).unwrap_or_else(|e| {
eprintln!("Could not open file: {e}");
std::process::exit(1);
});
file.read_to_end(&mut buffer).unwrap_or_else(|e| {
eprintln!("Could not read file: {e}");
std::process::exit(1);
});
closure(buffer.as_slice());
}

/// Fuzz a closure-like block of code by passing an object of arbitrary type.
Expand All @@ -137,13 +51,13 @@ macro_rules! read_args_and_fuzz {
#[cfg(not(any(feature = "afl", feature = "honggfuzz")))]
macro_rules! fuzz {
(|$buf:ident| $body:block) => {
$crate::read_args_and_fuzz!(|$buf| $body);
$crate::run_file(|$buf| $body);
};
(|$buf:ident: &[u8]| $body:block) => {
$crate::read_args_and_fuzz!(|$buf| $body);
$crate::run_file(|$buf| $body);
};
(|$buf:ident: $dty: ty| $body:block) => {
$crate::read_args_and_fuzz!(|$buf| {
$crate::run_file(|$buf| {
let $buf: $dty = {
let mut data = ::arbitrary::Unstructured::new($buf);
if let Ok(d) = ::arbitrary::Arbitrary::arbitrary(&mut data).map_err(|_| "") {
Expand Down

0 comments on commit 499e42e

Please sign in to comment.