Skip to content

Commit

Permalink
Support vanilla AFl directory format for casr-afl
Browse files Browse the repository at this point in the history
  • Loading branch information
headshog committed Feb 6, 2025
1 parent dc48b45 commit da76cd4
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 9 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,8 @@ Triage crashes after Sharpfuzz fuzzing with casr-afl:
$ casr-afl -i casr/tests/casr_tests/casrep/afl-out-sharpfuzz -o casr/tests/tmp_tests_casr/casr_afl_csharp_out
$ # You may force your own run arguments using --ignore-cmdline
$ casr-afl --ignore-cmdline -i casr/tests/casr_tests/casrep/afl-out-sharpfuzz -o casr/tests/tmp_tests_casr/casr_afl_csharp_out -- dotnet run --no-build --project /tmp/test_casr_afl_csharp/test_casr_afl_csharp.csproj @@
$ # If you use vanilla AFL for fuzzing with Sharpfuzz, force your own run arguments via -- <ARGS>
$ casr-afl -i casr/tests/casr_tests/casrep/afl-out-sharpfuzz/afl_main-worker -o casr/tests/tmp_tests_casr/casr_afl_csharp_out -- dotnet run --no-build --project /tmp/test_casr_afl_csharp/test_casr_afl_csharp.csproj @@

Triage libFuzzer crashes with casr-libfuzzer:

Expand Down
26 changes: 18 additions & 8 deletions casr/src/bin/casr-afl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,16 +134,26 @@ fn main() -> Result<()> {
Vec::new()
};

if args.is_empty() && matches.get_flag("ignore-cmdline") {
bail!("ARGS is empty, but \"ignore-cmdline\" option is provided.");
}

let hint = matches.get_one::<String>("hint").unwrap();
let input = matches.get_one::<PathBuf>("input").unwrap();
let afl_dir = input.join("crashes").is_dir();
let ignore_cmdline = matches.get_flag("ignore-cmdline") || afl_dir;

if args.is_empty() && ignore_cmdline {
if afl_dir {
bail!("ARGS is empty, but vanilla AFL directory is given as input.");

Check warning on line 144 in casr/src/bin/casr-afl.rs

View check run for this annotation

Codecov / codecov/patch

casr/src/bin/casr-afl.rs#L143-L144

Added lines #L143 - L144 were not covered by tests
} else {
bail!("ARGS is empty, but \"ignore-cmdline\" option is provided.");

Check warning on line 146 in casr/src/bin/casr-afl.rs

View check run for this annotation

Codecov / codecov/patch

casr/src/bin/casr-afl.rs#L146

Added line #L146 was not covered by tests
}
}

// Get all crashes.
let mut crashes: HashMap<String, CrashInfo> = HashMap::new();
for node_dir in fs::read_dir(matches.get_one::<PathBuf>("input").unwrap())? {
let path = node_dir?.path();
for node_dir in fs::read_dir(input)? {
let mut path = node_dir?.path();
if afl_dir {
path.pop();
}
if !path.is_dir() {
continue;
}
Expand All @@ -152,7 +162,7 @@ fn main() -> Result<()> {
let mut crash_info = casr::triage::CrashInfo {
..Default::default()
};
crash_info.target_args = if matches.get_flag("ignore-cmdline") {
crash_info.target_args = if ignore_cmdline {
args.clone()
} else {
let cmdline_path = path.join("cmdline");
Expand Down Expand Up @@ -219,7 +229,7 @@ fn main() -> Result<()> {
}
}

if matches.get_flag("ignore-cmdline") || !is_casr_gdb {
if ignore_cmdline || !is_casr_gdb {
args = Vec::new();
}

Expand Down
114 changes: 114 additions & 0 deletions casr/tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6164,6 +6164,120 @@ fn test_casr_afl_csharp_ignore_cmd() {
let _ = fs::remove_dir_all(&paths[5]);
}

#[test]
#[cfg(target_arch = "x86_64")]
fn test_casr_afl_csharp_vanilla_afl() {
use std::collections::HashMap;

let paths = [
abs_path("tests/casr_tests/casrep/afl-out-sharpfuzz/afl_main-worker"),
abs_path("tests/tmp_tests_casr/casr_afl_csharp_afl_dir_out"),
abs_path("tests/casr_tests/csharp/test_casr_afl_csharp"),
abs_path("tests/casr_tests/csharp/test_casr_afl_csharp_module"),
abs_path("tests/tmp_tests_casr/test_casr_afl_csharp"),
abs_path("tests/tmp_tests_casr/test_casr_afl_csharp_module"),
];

let _ = fs::remove_dir_all(&paths[1]);
let _ = fs::remove_dir_all(&paths[4]);
let _ = fs::remove_dir_all(&paths[5]);
let _ = fs::create_dir(abs_path("tests/tmp_tests_casr"));
let _ = copy_dir(&paths[2], &paths[4]).unwrap();
let _ = copy_dir(&paths[3], &paths[5]).unwrap();
let Ok(dotnet_path) = which::which("dotnet") else {
panic!("No dotnet is found.");
};

let _ = Command::new(dotnet_path.to_str().unwrap())
.args([
"build",
&format!("{}/test_casr_afl_csharp.csproj", &paths[4]),
])
.output()
.expect("dotnet build crashed");

let bins = Path::new(*EXE_CASR_AFL.read().unwrap()).parent().unwrap();
let mut output = Command::new(*EXE_CASR_AFL.read().unwrap());
output
.args([
"-i",
&paths[0],
"-o",
&paths[1],
"--",
(dotnet_path.to_str().unwrap()),
"run",
"--no-build",
"--project",
&format!("{}/test_casr_afl_csharp.csproj", &paths[4]),
"@@",
])
.env(
"PATH",
format!("{}:{}", bins.display(), std::env::var("PATH").unwrap()),
);

let output = output.output().expect("casr-afl crashed");

assert!(
output.status.success(),
"Stdout {}.\n Stderr: {}",
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
);
let res = String::from_utf8_lossy(&output.stderr);

assert!(!res.is_empty());

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

assert_eq!(unique_cnt, 3, "Invalid number of deduplicated reports");

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

assert_eq!(clusters_cnt, 3, "Invalid number of 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));
let _ = fs::remove_dir_all(&paths[4]);
let _ = fs::remove_dir_all(&paths[5]);
}

#[test]
#[cfg(target_arch = "x86_64")]
fn test_casr_libfuzzer_libafl() {
Expand Down
11 changes: 10 additions & 1 deletion docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -571,9 +571,18 @@ Sharpfuzz example (with --ignore-cmdline):
$ dotnet publish /tmp/test_casr_afl_csharp/test_casr_afl_csharp.csproj -c Debug -o /tmp/test_casr_afl_csharp/bin
$ casr-afl --ignore-cmdline -i casr/tests/casr_tests/casrep/afl-out-sharpfuzz -o casr/tests/tmp_tests_casr/casr_afl_csharp_out -- dotnet run --no-build --project /tmp/test_casr_afl_csharp/test_casr_afl_csharp.csproj @@

**NOTE:** if you run `casr-afl` for Sharpfuzz pipeline using `--ignore-cmdline` with `dotnet run`, build
Sharpfuzz example (with vanilla AFL directory):

$ cp -r casr/tests/casr_tests/csharp/test_casr_afl_csharp /tmp/test_casr_afl_csharp
$ cp -r casr/tests/casr_tests/csharp/test_casr_afl_csharp_module /tmp/test_casr_afl_csharp_module
$ dotnet publish /tmp/test_casr_afl_csharp/test_casr_afl_csharp.csproj -c Debug -o /tmp/test_casr_afl_csharp/bin
$ casr-afl -i casr/tests/casr_tests/casrep/afl-out-sharpfuzz/afl_main-worker -o casr/tests/tmp_tests_casr/casr_afl_csharp_out -- dotnet run --no-build --project /tmp/test_casr_afl_csharp/test_casr_afl_csharp.csproj @@

**NOTE 1:** if you run `casr-afl` for Sharpfuzz pipeline using `--ignore-cmdline` with `dotnet run`, build
your project before (via `dotnet build` or `dotnet publish`) and specify `--no-build` option for `dotnet run`.

**NOTE 2:** if you run `casr-afl` for Sharpfuzz pipeline using vanilla AFL input directory, force your own run arguments via `-- <ARGS>`.

## casr-libfuzzer

Triage crashes found by libFuzzer based fuzzer
Expand Down

0 comments on commit da76cd4

Please sign in to comment.