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 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
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"

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"
9 changes: 9 additions & 0 deletions casr/src/bin/casr-afl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,15 @@ 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("join")
.long("join")
.env("CASR_PREV_CLUSTERS_DIR")
.action(ArgAction::Set)
.value_parser(clap::value_parser!(PathBuf))
.value_name("PREV_CLUSTERS_DIR")
.help("Use directory with previously triaged reports for new reports accumulation")
)
.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
9 changes: 9 additions & 0 deletions casr/src/bin/casr-libfuzzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,15 @@ fn main() -> Result<()> {
.value_name("OUTPUT_DIR")
.help("Output directory with triaged reports")
)
.arg(
Arg::new("join")
.long("join")
.env("CASR_PREV_CLUSTERS_DIR")
.action(ArgAction::Set)
.value_parser(clap::value_parser!(PathBuf))
.value_name("PREV_CLUSTERS_DIR")
.help("Use directory with previously triaged reports for new reports accumulation")
)
.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 @@
bail!("No crashes found");
}

let accum_mode = matches.contains_id("join");

let output_dir = initialize_dirs(matches)?;

let casrep_dir = if accum_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 @@
.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 @@
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 @@
}

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 accum_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()

Check warning on line 258 in casr/src/triage.rs

View check run for this annotation

Codecov / codecov/patch

casr/src/triage.rs#L256-L258

Added lines #L256 - L258 were not covered by tests
);
}

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()

Check warning on line 291 in casr/src/triage.rs

View check run for this annotation

Codecov / codecov/patch

casr/src/triage.rs#L289-L291

Added lines #L289 - L291 were not covered by tests
);
}

// 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 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 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(join_dir) = matches.get_one::<PathBuf>("join") {
copy_dir(join_dir, output_dir)
.with_context(|| format!("Couldn't copy join directory {}", join_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());

Check warning on line 334 in casr/src/util.rs

View check run for this annotation

Codecov / codecov/patch

casr/src/util.rs#L334

Added line #L334 was not covered by tests
}
} else if !output_dir.exists() && fs::create_dir_all(output_dir).is_err() {
format!("Couldn't create output directory {}", output_dir.display());

Check warning on line 337 in casr/src/util.rs

View check run for this annotation

Codecov / codecov/patch

casr/src/util.rs#L337

Added line #L337 was not covered by tests
}

// 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