Skip to content

Commit

Permalink
Solve merge comfilct
Browse files Browse the repository at this point in the history
  • Loading branch information
hkctkuy committed Jun 4, 2024
1 parent 5f871aa commit 9952393
Show file tree
Hide file tree
Showing 7 changed files with 214 additions and 52 deletions.
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"

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(
Arg::new("seed")
.short('s')
.long("seed")
.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
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("seed")
.short('s')
.long("seed")
.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_c = 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_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()
);
}

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
29 changes: 19 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,36 @@ 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)
.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 {
fs::create_dir_all(output_dir).with_context(|| {
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
110 changes: 106 additions & 4 deletions casr/tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3951,32 +3951,32 @@ fn test_casr_libfuzzer() {

let paths = [
abs_path("tests/casr_tests/casrep/libfuzzer_crashes_xlnt"),
abs_path("tests/tmp_tests_casr/casr_libfuzzer_seed"),
abs_path("tests/tmp_tests_casr/casr_libfuzzer_out"),
abs_path("tests/casr_tests/bin/load_fuzzer"),
];

let _ = fs::remove_dir_all(&paths[1]);
let _ = fs::create_dir(abs_path("tests/tmp_tests_casr"));

let bins = Path::new(*EXE_CASR_LIBFUZZER.read().unwrap())
.parent()
.unwrap();
let mut cmd = Command::new(*EXE_CASR_LIBFUZZER.read().unwrap());
cmd.args(["-i", &paths[0], "-o", &paths[1], "--", &paths[2]])
cmd.args(["-i", &paths[0], "-o", &paths[1], "-f", "--", &paths[3]])
.env(
"PATH",
format!("{}:{}", bins.display(), std::env::var("PATH").unwrap()),
);
let output = cmd.output().expect("failed to start casr-libfuzzer");

let output = cmd.output().expect("failed to start casr-libfuzzer");
assert!(
output.status.success(),
"Stdout {}.\n Stderr: {}",
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
);
let err = String::from_utf8_lossy(&output.stderr);

let err = String::from_utf8_lossy(&output.stderr);
assert!(!err.is_empty());

assert!(err.contains("casr-san: No crash on input"));
Expand Down Expand Up @@ -4036,6 +4036,108 @@ fn test_casr_libfuzzer() {
}

assert!(storage.values().all(|x| *x > 1));

// Remove several clusters
let cluster_paths = [
abs_path("tests/tmp_tests_casr/casr_libfuzzer_seed/cl2"),
abs_path("tests/tmp_tests_casr/casr_libfuzzer_seed/cl20"),
abs_path("tests/tmp_tests_casr/casr_libfuzzer_seed/cl21"),
abs_path("tests/tmp_tests_casr/casr_libfuzzer_seed/cl22"),
abs_path("tests/tmp_tests_casr/casr_libfuzzer_seed/cl23"),
];
for path in cluster_paths {
let _ = fs::remove_dir_all(path);
}

let mut cmd = Command::new(*EXE_CASR_LIBFUZZER.read().unwrap());
cmd.args([
"-i", &paths[0], "-s", &paths[1], "-o", &paths[2], "-f", "--", &paths[3],
])
.env(
"PATH",
format!("{}:{}", bins.display(), std::env::var("PATH").unwrap()),
);

let output = cmd.output().expect("failed to start casr-libfuzzer");
assert!(
output.status.success(),
"Stdout {}.\n Stderr: {}",
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
);

let err = String::from_utf8_lossy(&output.stderr);
assert!(!err.is_empty());

assert!(err.contains("casr-san: No crash on input"));
assert!(err.contains("1 out of memory seeds are saved to"));
assert!(err.contains("EXPLOITABLE"));
assert!(err.contains("NOT_EXPLOITABLE"));
assert!(err.contains("PROBABLY_EXPLOITABLE"));
assert!(err.contains("heap-buffer-overflow(read)"));
assert!(err.contains("heap-buffer-overflow(write)"));
assert!(err.contains("DestAvNearNull"));
assert!(err.contains("xml::serialization"));
assert!(err.contains("AbortSignal"));
assert!(err.contains("compound_document.hpp:83"));

let re = Regex::new(r"Number of duplicates: (?P<unique>\d+)").unwrap();
let duplicates_cnt = re
.captures(&err)
.unwrap()
.name("unique")
.map(|x| x.as_str())
.unwrap()
.parse::<u32>()
.unwrap();

assert_eq!(duplicates_cnt, 28, "Invalid number of duplicates");

let re = Regex::new(r"Number of new clusters: (?P<clusters>\d+)").unwrap();
let clusters_cnt = re
.captures(&err)
.unwrap()
.name("clusters")
.map(|x| x.as_str())
.unwrap()
.parse::<u32>()
.unwrap();

assert_eq!(clusters_cnt, 5, "Invalid number of new clusters");

let re = Regex::new(r"Number of reports in new clusters: (?P<clusters>\d+)").unwrap();
let reports_cnt = re
.captures(&err)
.unwrap()
.name("clusters")
.map(|x| x.as_str())
.unwrap()
.parse::<u32>()
.unwrap();

assert_eq!(reports_cnt, 17, "Invalid number of reports in new clusters");

let mut storage: HashMap<String, u32> = HashMap::new();
for entry in fs::read_dir(&paths[1]).unwrap() {
let e = entry.unwrap().path();
let fname = e.file_name().unwrap().to_str().unwrap();
if fname.starts_with("cl") && e.is_dir() {
for file in fs::read_dir(e).unwrap() {
let mut e = file.unwrap().path();
if e.is_file() && e.extension().is_some() && e.extension().unwrap() == "casrep" {
e = e.with_extension("");
}
let fname = e.file_name().unwrap().to_str().unwrap();
if let Some(v) = storage.get_mut(fname) {
*v += 1;
} else {
storage.insert(fname.to_string(), 1);
}
}
}
}

assert!(storage.values().all(|x| *x > 1));
}

#[test]
Expand Down

0 comments on commit 9952393

Please sign in to comment.