Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Small various fixes #112

Merged
merged 11 commits into from
Dec 6, 2024
Merged
101 changes: 100 additions & 1 deletion 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 @@ -27,6 +27,7 @@ libc = { version = "0.2.153", optional = true }
semver = { version = "1.0.23", optional = true }
strip-ansi-escapes = { version = "0.2.0", optional = true }
time-humanize = { version = "0.1.3", optional = true }
twox-hash = { version = "2.0.1", optional = true }

[features]
default = ["cli"]
Expand All @@ -40,6 +41,7 @@ cli = [
"libc",
"time-humanize",
"cargo_metadata",
"twox-hash",
]
coverage = ["fork", "libc"]

Expand Down
21 changes: 12 additions & 9 deletions src/bin/cargo-ziggy/coverage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ impl Cover {

let _ = process::Command::new(format!("./target/coverage/debug/{}", &self.target))
.arg(format!("{}", shared_corpus.display()))
.env(
"LLVM_PROFILE_FILE",
"target/coverage/debug/deps/coverage-%p-%m.profraw",
)
.spawn()
.unwrap()
.wait_with_output()
Expand Down Expand Up @@ -84,23 +88,23 @@ impl Cover {
let cargo = env::var("CARGO").unwrap_or_else(|_| String::from("cargo"));

let mut coverage_rustflags = env::var("COVERAGE_RUSTFLAGS")
.unwrap_or_else(|_| String::from("--cfg=coverage -Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort"));
.unwrap_or_else(|_| String::from("-Cinstrument-coverage"));
coverage_rustflags.push_str(&env::var("RUSTFLAGS").unwrap_or_default());

process::Command::new(cargo)
let build = process::Command::new(cargo)
.args([
"rustc",
"--target-dir=target/coverage",
"--features=ziggy/coverage",
])
.env("RUSTFLAGS", coverage_rustflags)
.env("RUSTDOCFLAGS", "-Cpanic=unwind")
.env("CARGO_INCREMENTAL", "0")
.env("RUSTC_BOOTSTRAP", "1") // Trick to avoid forcing user to use rust nightly
.spawn()
.context("⚠️ couldn't spawn rustc for coverage")?
.wait()
.context("⚠️ couldn't wait for the rustc during coverage")?;
if !build.success() {
return Err(anyhow!("⚠️ build failed"));
}
Ok(())
}

Expand Down Expand Up @@ -129,11 +133,10 @@ impl Cover {
}

pub fn clean_old_cov() -> Result<(), anyhow::Error> {
if let Ok(gcda_files) = glob("target/coverage/debug/deps/*.gcda") {
for file in gcda_files.flatten() {
if let Ok(profile_files) = glob("target/coverage/debug/deps/*.profraw") {
for file in profile_files.flatten() {
let file_string = &file.display();
fs::remove_file(&file)
.context(format!("⚠️ couldn't find {} during coverage", file_string))?;
fs::remove_file(&file).context(format!("⚠️ couldn't remove {}", file_string))?;
}
}
Ok(())
Expand Down
88 changes: 53 additions & 35 deletions src/bin/cargo-ziggy/fuzz.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,10 @@ use std::{
time::{Duration, Instant, SystemTime, UNIX_EPOCH},
};
use strip_ansi_escapes::strip_str;
use twox_hash::XxHash64;

/// Main logic for managing fuzzers and the fuzzing process in ziggy.

/// ## Initial minimization logic

/// When launching fuzzers, if initial corpora exist, they are merged together and we minimize it
/// with both AFL++ and Honggfuzz.
/// ```text
Expand All @@ -34,7 +33,6 @@ use strip_ansi_escapes::strip_str;
/// honggfuzz -i corpus -o corpus
/// ```
/// The `all_afl_corpora` directory corresponds to the `output/target_name/afl/**/queue/` directories.

impl Fuzz {
pub fn corpus(&self) -> String {
self.corpus
Expand Down Expand Up @@ -153,7 +151,7 @@ impl Fuzz {

self.start_time = Instant::now();

let mut last_synced_queue_id: u32 = 0;
let mut last_synced_created_time: Option<SystemTime> = None;
let mut last_sync_time = Instant::now();
let mut afl_output_ok = false;

Expand Down Expand Up @@ -301,26 +299,55 @@ impl Fuzz {
}

// If both fuzzers are running, we copy over AFL++'s queue for consumption by Honggfuzz.
// Otherwise, if only AFL++ is up we copy AFL++'s queue to the global corpus.
// We do this every 10 seconds
if self.afl() && last_sync_time.elapsed().as_secs() > 10 {
let afl_corpus = glob(&format!(
"{}/afl/mainaflfuzzer/queue/*",
self.output_target(),
))?
.flatten();
for file in afl_corpus {
if let Some((file_id, file_name)) = extract_file_id(&file) {
if file_id > last_synced_queue_id {
let copy_destination = match self.honggfuzz() {
true => format!("{}/queue/{file_name}", self.output_target()),
false => format!("{}/corpus/{file_name}", self.output_target()),
};
let _ = fs::copy(&file, copy_destination);
last_synced_queue_id = file_id;
// We also copy-over each live corpus to the shared corpus directory, where each file
// name is the md5 hash of the file. This happens every 10 minutes.
if last_sync_time.elapsed().as_secs() > 10 * 60 {
let mut files = vec![];
if self.afl() {
files.append(
&mut glob(&format!(
"{}/afl/mainaflfuzzer/queue/*",
self.output_target(),
))?
.flatten()
.collect(),
);
}
if self.honggfuzz() {
files.append(
&mut glob(&format!("{}/honggfuzz/corpus/*", self.output_target(),))?
.flatten()
.collect(),
);
}
let mut newest_time = last_synced_created_time;
let valid_files = files.iter().filter(|file| {
if let Ok(metadata) = file.metadata() {
let created = metadata.created().unwrap();
if last_synced_created_time.is_none_or(|time| created > time) {
if newest_time.is_none_or(|time| created > time) {
newest_time = Some(created);
}
return true;
}
}
false
});
for file in valid_files {
if let Some(file_name) = file.file_name() {
if self.honggfuzz() {
let _ = fs::copy(
file,
format!("{}/queue/{:?}", self.output_target(), file_name),
);
}
// Hash the file to get its file name
let bytes = fs::read(file).unwrap_or_default();
let hash = XxHash64::oneshot(0, &bytes);
let _ = fs::copy(file, format!("{}/corpus/{hash:x}", self.output_target()));
}
}
last_synced_created_time = newest_time;
last_sync_time = Instant::now();
}

Expand Down Expand Up @@ -568,9 +595,9 @@ impl Fuzz {
.env(
"HFUZZ_RUN_ARGS",
format!(
"--input={} -o{} -n{honggfuzz_jobs} -F{} --dynamic_input={}/queue {timeout_option} {dictionary_option}",
&self.corpus(),
"--input={} -o{}/honggfuzz/corpus -n{honggfuzz_jobs} -F{} --dynamic_input={}/queue {timeout_option} {dictionary_option}",
&self.corpus(),
&self.output_target(),
self.max_length,
self.output_target(),
),
Expand Down Expand Up @@ -664,7 +691,9 @@ impl Fuzz {
.map_or(String::from("err"), |corpus| format!("{}", corpus.count()));

let engine = match (self.no_afl, self.no_honggfuzz, self.jobs) {
(false, _, _) => FuzzingEngines::AFLPlusPlus,
(false, false, 1) => FuzzingEngines::AFLPlusPlus,
(false, false, _) => FuzzingEngines::All,
(false, true, _) => FuzzingEngines::AFLPlusPlus,
(true, false, _) => FuzzingEngines::Honggfuzz,
(true, true, _) => return Err(anyhow!("Pick at least one fuzzer")),
};
Expand Down Expand Up @@ -960,14 +989,3 @@ pub fn stop_fuzzers(processes: &mut Vec<process::Child>) -> Result<(), Error> {
}
Ok(())
}

pub fn extract_file_id(file: &Path) -> Option<(u32, String)> {
let file_name = file.file_name()?.to_str()?;
if file_name.len() < 9 {
return None;
}
let (id_part, _) = file_name.split_at(9);
let str_id = id_part.strip_prefix("id:")?;
let file_id = str_id.parse::<u32>().ok()?;
Some((file_id, String::from(file_name)))
}
3 changes: 2 additions & 1 deletion src/bin/cargo-ziggy/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pub const DEFAULT_UNMODIFIED_TARGET: &str = "automatically guessed";

#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
pub enum FuzzingEngines {
All,
AFLPlusPlus,
Honggfuzz,
}
Expand Down Expand Up @@ -242,7 +243,7 @@ pub struct Minimize {
#[clap(short, long, value_name = "NUM", default_value_t = 1)]
jobs: u32,

#[clap(short, long, value_enum, default_value_t = FuzzingEngines::AFLPlusPlus)]
#[clap(short, long, value_enum, default_value_t = FuzzingEngines::All)]
engine: FuzzingEngines,
}

Expand Down
Loading
Loading