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

Support report accumulation for casr-libfuzzer and casr-afl #223

Merged
merged 10 commits into from
Jun 12, 2024
Merged
Show file tree
Hide file tree
Changes from 6 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
2 changes: 1 addition & 1 deletion casr/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ tokio = { version = "1", features = ["rt", "macros"], optional = true }
toml = { version = "0.8", optional = true }
wait-timeout = "0.2"
which = "6.0"
copy_dir = "0.1.3"
SweetVishnya marked this conversation as resolved.
Show resolved Hide resolved

libcasr = { path = "../libcasr", version = "2.12.0", features = ["serde", "exploitable"] }

Expand All @@ -52,4 +53,3 @@ required-features = ["dojo"]

[dev-dependencies]
lazy_static = "1.4"
copy_dir = "0.1.3"
10 changes: 10 additions & 0 deletions casr/src/bin/casr-afl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,16 @@ fn main() -> Result<()> {
.value_parser(clap::value_parser!(PathBuf))
.help("Output directory with triaged reports")
)
.arg(
SweetVishnya marked this conversation as resolved.
Show resolved Hide resolved
Arg::new("seed")
SweetVishnya marked this conversation as resolved.
Show resolved Hide resolved
.short('s')
.long("seed")
.env("CASR_SEED_DIR")
.action(ArgAction::Set)
.value_parser(clap::value_parser!(PathBuf))
.value_name("SEED_DIR")
.help("Seed directory with previously triaged reports")
)
.arg(
Arg::new("force-remove")
.short('f')
Expand Down
6 changes: 3 additions & 3 deletions casr/src/bin/casr-cluster.rs
Original file line number Diff line number Diff line change
Expand Up @@ -762,11 +762,11 @@ fn main() -> Result<()> {
println!("Number of new clusters: {result}");
}
// Print crashline dedup summary
if before != 0 {
if dedup_crashlines {
println!("Number of reports before crashline deduplication in new clusters: {before}");
}
if before != after {
println!("Number of reports after crashline deduplication in new clusters: {after}");
} else {
println!("Number of reports in new clusters: {after}");
}
let sil = calc_avg_sil(paths[1], jobs)?;
println!("Cluster silhouette score: {sil}");
Expand Down
10 changes: 10 additions & 0 deletions casr/src/bin/casr-libfuzzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,16 @@ fn main() -> Result<()> {
.value_name("OUTPUT_DIR")
.help("Output directory with triaged reports")
)
.arg(
Arg::new("seed")
.short('s')
.long("seed")
.env("CASR_SEED_DIR")
.action(ArgAction::Set)
.value_parser(clap::value_parser!(PathBuf))
.value_name("SEED_DIR")
.help("Seed directory with previously triaged reports")
)
.arg(
Arg::new("force-remove")
.short('f')
Expand Down
101 changes: 67 additions & 34 deletions casr/src/triage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,16 @@ pub fn fuzzing_crash_triage_pipeline(
bail!("No crashes found");
}

let seed_mode = matches.contains_id("seed");

let output_dir = initialize_dirs(matches)?;

let casrep_dir = if seed_mode {
output_dir.join("casrep")
} else {
output_dir.to_path_buf()
};

// Get timeout
let timeout = *matches.get_one::<u64>("timeout").unwrap();

Expand Down Expand Up @@ -189,7 +197,7 @@ pub fn fuzzing_crash_triage_pipeline(
.join(
|| {
crashes.par_iter().try_for_each(|(_, crash)| {
if let Err(e) = crash.run_casr(output_dir.as_path(), timeout) {
if let Err(e) = crash.run_casr(casrep_dir.as_path(), timeout) {
// Disable util::log_progress
*counter.write().unwrap() = total;
bail!(e);
Expand All @@ -210,7 +218,7 @@ pub fn fuzzing_crash_triage_pipeline(
info!("Deduplicating CASR reports...");
let casr_cluster_d = Command::new(&casr_cluster)
.arg("-d")
.arg(output_dir.clone().into_os_string())
.arg(casrep_dir.clone().into_os_string())
.output()
.with_context(|| format!("Couldn't launch {casr_cluster:?}"))?;

Expand All @@ -230,41 +238,66 @@ pub fn fuzzing_crash_triage_pipeline(
}

if !matches.get_flag("no-cluster") {
if output_dir
.read_dir()?
.flatten()
.map(|e| e.path())
.filter(|e| e.extension().is_some() && e.extension().unwrap() == "casrep")
.count()
< 2
{
info!("There are less than 2 CASR reports, nothing to cluster.");
return summarize_results(matches, crashes, gdb_args);
}
info!("Clustering CASR reports...");
let casr_cluster_c = Command::new(&casr_cluster)
.arg("-c")
.arg(output_dir.clone().into_os_string())
.output()
.with_context(|| format!("Couldn't launch {casr_cluster:?}"))?;
if seed_mode {
info!("Accumulating CASR reports...");
let casr_cluster_u = Command::new(&casr_cluster)
.arg("-u")
.arg(casrep_dir.clone().into_os_string())
.arg(output_dir.clone().into_os_string())
.output()
.with_context(|| format!("Couldn't launch {casr_cluster:?}"))?;

if casr_cluster_u.status.success() {
info!(
"{}",
String::from_utf8_lossy(&casr_cluster_u.stdout).trim_end()
);
} else {
error!(
"{}",
String::from_utf8_lossy(&casr_cluster_u.stderr).trim_end()
);
}

if casr_cluster_c.status.success() {
info!(
"{}",
String::from_utf8_lossy(&casr_cluster_c.stdout).trim_end()
);
// Remove reports from deduplication phase. They are in clusters now.
fs::remove_dir_all(casrep_dir)?;
} else {
error!(
"{}",
String::from_utf8_lossy(&casr_cluster_c.stderr).trim_end()
);
}
if casrep_dir
.read_dir()?
.flatten()
.map(|e| e.path())
.filter(|e| e.extension().is_some() && e.extension().unwrap() == "casrep")
.count()
< 2
{
info!("There are less than 2 CASR reports, nothing to cluster.");
return summarize_results(matches, crashes, gdb_args);
}
info!("Clustering CASR reports...");
let casr_cluster_c = Command::new(&casr_cluster)
.arg("-c")
.arg(output_dir.clone().into_os_string())
.output()
.with_context(|| format!("Couldn't launch {casr_cluster:?}"))?;

if casr_cluster_c.status.success() {
info!(
"{}",
String::from_utf8_lossy(&casr_cluster_c.stdout).trim_end()
);
} else {
error!(
"{}",
String::from_utf8_lossy(&casr_cluster_c.stderr).trim_end()
);
}

// Remove reports from deduplication phase. They are in clusters now.
for casrep in fs::read_dir(output_dir)?.flatten().map(|e| e.path()) {
if let Some(ext) = casrep.extension() {
if ext == "casrep" {
let _ = fs::remove_file(casrep);
// Remove reports from deduplication phase. They are in clusters now.
for casrep in fs::read_dir(casrep_dir)?.flatten().map(|e| e.path()) {
if let Some(ext) = casrep.extension() {
if ext == "casrep" {
let _ = fs::remove_file(casrep);
}
}
}
}
Expand Down
27 changes: 17 additions & 10 deletions casr/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use libcasr::stacktrace::{

use anyhow::{bail, Context, Result};
use clap::ArgMatches;
use copy_dir::copy_dir;
use gdb_command::stacktrace::StacktraceExt;
use is_executable::IsExecutable;
use log::{info, warn};
Expand Down Expand Up @@ -316,28 +317,34 @@ pub fn get_atheris_lib() -> Result<String> {
pub fn initialize_dirs(matches: &clap::ArgMatches) -> Result<&PathBuf> {
// Get output dir
let output_dir = matches.get_one::<PathBuf>("output").unwrap();
if !output_dir.exists() {
fs::create_dir_all(output_dir).with_context(|| {
format!("Couldn't create output directory {}", output_dir.display())
})?;
} else if output_dir.read_dir()?.next().is_some() {
if output_dir.exists() && output_dir.read_dir()?.next().is_some() {
if matches.get_flag("force-remove") {
fs::remove_dir_all(output_dir)?;
fs::create_dir_all(output_dir).with_context(|| {
format!("Couldn't create output directory {}", output_dir.display())
})?;
} else {
bail!("Output directory is not empty.");
}
}

if let Some(seed_dir) = matches.get_one::<PathBuf>("seed") {
copy_dir(seed_dir, output_dir)
SweetVishnya marked this conversation as resolved.
Show resolved Hide resolved
.with_context(|| format!("Couldn't copy seed directory {}", seed_dir.display()))?;
// Get casrep dir
let casrep_dir = output_dir.join("casrep");
if !casrep_dir.exists() && fs::create_dir_all(&casrep_dir).is_err() {
bail!("Failed to create dir {}", &casrep_dir.to_str().unwrap());
}
} else if !output_dir.exists() && fs::create_dir_all(output_dir).is_err() {
format!("Couldn't create output directory {}", output_dir.display());
}

// Get oom dir
let oom_dir = output_dir.join("oom");
if fs::create_dir_all(&oom_dir).is_err() {
if !oom_dir.exists() && fs::create_dir_all(&oom_dir).is_err() {
bail!("Failed to create dir {}", &oom_dir.to_str().unwrap());
}
// Get timeout dir
let timeout_dir = output_dir.join("timeout");
if fs::create_dir_all(&timeout_dir).is_err() {
if !timeout_dir.exists() && fs::create_dir_all(&timeout_dir).is_err() {
bail!("Failed to create dir {}", &timeout_dir.to_str().unwrap());
}

Expand Down
Loading
Loading