Skip to content

Commit

Permalink
Support libafl pipeline in casr-libfuzzer (#233)
Browse files Browse the repository at this point in the history
  • Loading branch information
headshog authored Jan 29, 2025
1 parent a25433e commit dc48b45
Show file tree
Hide file tree
Showing 130 changed files with 156 additions and 15 deletions.
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ and AFL-based fuzzer [Sharpfuzz](https://github.com/Metalnem/sharpfuzz).
[libFuzzer](https://www.llvm.org/docs/LibFuzzer.html) based fuzzer
(C/C++/[go-fuzz](https://github.com/dvyukov/go-fuzz)/[Atheris](https://github.com/google/atheris)
/[Jazzer](https://github.com/CodeIntelligenceTesting/jazzer)/[Jazzer.js](https://github.com/CodeIntelligenceTesting/jazzer.js)/
[jsfuzz](https://github.com/fuzzitdev/jsfuzz)).
[jsfuzz](https://github.com/fuzzitdev/jsfuzz)) or [LibAFL](https://github.com/AFLplusplus/LibAFL)
based [fuzzers](https://github.com/AFLplusplus/LibAFL/tree/main/fuzzers).
`casr-dojo` allows to upload new and unique CASR reports to
[DefectDojo](https://github.com/DefectDojo/django-DefectDojo) (available with
`dojo` feature).
Expand Down Expand Up @@ -228,6 +229,10 @@ Triage Jazzer.js crashes with casr-libfuzzer (Jazzer.js installation [guide](htt
$ sudo npm install --save-dev @jazzer.js/core
$ casr-libfuzzer -i ./xml2js -o casr/tests/tmp_tests_casr/xml2js_fuzzer_out/out -- npx jazzer casr/tests/tmp_tests_casr/xml2js_fuzzer_out/xml2js_fuzzer.js

Triage LibAFL crashes with casr-libfuzzer:

$ casr-libfuzzer -i casr/tests/casr_tests/casrep/test_libafl_crashes -o casr/tests/tmp_tests_casr/casr_libafl_out -- casr/tests/casr_tests/bin/test_libafl_fuzzer @@

Upload new and unique CASR reports to
[DefectDojo](https://github.com/DefectDojo/django-DefectDojo):

Expand Down Expand Up @@ -260,8 +265,8 @@ fuzzer [Sharpfuzz](https://www.llvm.org/docs/LibFuzzer.html), the pipeline
If you use [libFuzzer](https://www.llvm.org/docs/LibFuzzer.html) based fuzzer
(C/C++/[go-fuzz](https://github.com/dvyukov/go-fuzz)/[Atheris](https://github.com/google/atheris)
/[Jazzer](https://github.com/CodeIntelligenceTesting/jazzer)/[Jazzer.js](https://github.com/CodeIntelligenceTesting/jazzer.js)/
[jsfuzz](https://github.com/fuzzitdev/jsfuzz)), the pipeline (without `casr-ubsan` and `casr-dojo`) could be done automatically
by `casr-libfuzzer`.
[jsfuzz](https://github.com/fuzzitdev/jsfuzz)) or [LibAFL](https://github.com/AFLplusplus/LibAFL) based fuzzer,
the pipeline (without `casr-ubsan` and `casr-dojo`) could be done automatically by `casr-libfuzzer`.

## Contributing

Expand Down
30 changes: 23 additions & 7 deletions casr/src/bin/casr-libfuzzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use std::path::{Path, PathBuf};
fn main() -> Result<()> {
let matches = clap::Command::new("casr-libfuzzer")
.version(clap::crate_version!())
.about("Triage crashes found by libFuzzer based fuzzer (C/C++/go-fuzz/Atheris/Jazzer/Jazzer.js/jsfuzz)")
.about("Triage crashes found by libFuzzer based fuzzer (C/C++/go-fuzz/Atheris/Jazzer/Jazzer.js/jsfuzz) or LibAFL based fuzzer")
.term_width(90)
.arg(
Arg::new("log-level")
Expand Down Expand Up @@ -48,7 +48,7 @@ fn main() -> Result<()> {
.action(ArgAction::Set)
.default_value(".")
.value_name("INPUT_DIR")
.help("Directory containing crashes found by libFuzzer")
.help("Directory containing crashes found by libFuzzer or LibAFL")
.value_parser(move |arg: &str| {
let i_dir = Path::new(arg);
if !i_dir.exists() {
Expand Down Expand Up @@ -185,18 +185,34 @@ fn main() -> Result<()> {
argv.len() - 1
};

// Get all crashes.
let crashes: HashMap<String, CrashInfo> = fs::read_dir(input_dir)?
let crash_files: HashMap<String, PathBuf> = fs::read_dir(input_dir)?
.flatten()
.map(|p| p.path())
.filter(|p| p.is_file())
.map(|p| (p.file_name().unwrap().to_str().unwrap().to_string(), p))
.filter(|(fname, _)| fname.starts_with("crash-") || fname.starts_with("leak-"))
.collect();

// Determine crash directory format for libfuzzer or LibAFL.
let crash_filter = if crash_files
.iter()
.any(|(fname, _)| fname.starts_with("crash-") || fname.starts_with("leak-"))
{
|arg: &(&std::string::String, &PathBuf)| {
arg.0.starts_with("crash-") || arg.0.starts_with("leak-")
}
} else {
|arg: &(&std::string::String, &PathBuf)| !arg.0.starts_with(".")
};

// Get all crashes.
let crashes: HashMap<String, CrashInfo> = crash_files
.iter()
.filter(crash_filter)
.map(|(fname, p)| {
(
fname,
fname.clone(),
CrashInfo {
path: p,
path: p.to_path_buf(),
target_args: argv.iter().map(|x| x.to_string()).collect(),
envs: envs.clone(),
at_index: Some(at_index),
Expand Down
Binary file added casr/tests/casr_tests/bin/test_libafl_fuzzer
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
bae!qcdaaadaqc�
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
bad�!!��dbcd�dedcdd!��dbcd�dddcdd�d�{ccc
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
badd!bbbabcdabcdb
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ba�!bdddddddddddd��
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ba�!bbad����� ����� �!bd
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ba�!bdt!bdt
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
bay!aaaaaaaaaOaaaaab9OOOOOO
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
badd!bdddddddddddd{�d
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ba�!`da���|�"c@
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
bad�! !!!!!!!!!!!!!!!!!!
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ba!!b!!!!!!!!!!!!!!b!!!!!!!
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
bada!

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ba�!bddbad����dddddd!c
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ba!b����
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
99 changes: 99 additions & 0 deletions casr/tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6163,3 +6163,102 @@ fn test_casr_afl_csharp_ignore_cmd() {
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() {
use std::collections::HashMap;

let paths = [
abs_path("tests/casr_tests/casrep/test_libafl_crashes"),
abs_path("tests/tmp_tests_casr/casr_libafl_out"),
];

let _ = fs::remove_dir_all(&paths[1]);
let _ = fs::create_dir(abs_path("tests/tmp_tests_casr"));
let _ = fs::copy(
abs_path("tests/casr_tests/bin/test_libafl_fuzzer"),
"/tmp/test_libafl_fuzzer",
);

let bins = Path::new(*EXE_CASR_LIBFUZZER.read().unwrap())
.parent()
.unwrap();
let output = Command::new(*EXE_CASR_LIBFUZZER.read().unwrap())
.args([
"-i",
&paths[0],
"-o",
&paths[1],
"--",
"/tmp/test_libafl_fuzzer",
"@@",
])
.env(
"PATH",
format!("{}:{}", bins.display(), std::env::var("PATH").unwrap()),
)
.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 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, 5, "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, 5, "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("");
if e.extension().is_some() && e.extension().unwrap() == "gdb" {
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 == 2));
let _ = fs::remove_file("/tmp/test_libafl_fuzzer");
let _ = fs::remove_dir_all(&paths[1]);
}
16 changes: 11 additions & 5 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ crashes found by [AFL++](https://github.com/AFLplusplus/AFLplusplus) and
AFL-based fuzzer [Sharpfuzz](https://github.com/Metalnem/sharpfuzz).
`casr-libfuzzer` can triage crashes found by
[libFuzzer](https://www.llvm.org/docs/LibFuzzer.html) (libFuzzer, go-fuzz,
Atheris, Jazzer, Jazzer.js, jsfuzz). `casr-dojo` allows to upload new and
unique CASR reports to [DefectDojo](https://github.com/DefectDojo/django-DefectDojo).
Atheris, Jazzer, Jazzer.js, jsfuzz) or by [LibAFL](https://github.com/AFLplusplus/LibAFL) based
[fuzzers](https://github.com/AFLplusplus/LibAFL/tree/main/fuzzers).
`casr-dojo` allows to upload new and unique CASR reports to [DefectDojo](https://github.com/DefectDojo/django-DefectDojo).
`casr-cli` is meant to provide TUI for viewing reports and converting them into
SARIF report. Reports triage (deduplication, clustering) is done by `casr-cluster`.

Expand Down Expand Up @@ -576,7 +577,7 @@ your project before (via `dotnet build` or `dotnet publish`) and specify `--no-b
## casr-libfuzzer

Triage crashes found by libFuzzer based fuzzer
(C/C++/go-fuzz/Atheris/Jazzer/Jazzer.js/jsfuzz)
(C/C++/go-fuzz/Atheris/Jazzer/Jazzer.js/jsfuzz) or LibAFL based fuzzer

Usage: casr-libfuzzer [OPTIONS] --output <OUTPUT_DIR> -- <ARGS>...

Expand All @@ -592,7 +593,7 @@ Triage crashes found by libFuzzer based fuzzer
Timeout (in seconds) for target execution, 0 means that timeout is disabled
[default: 0]
-i, --input <INPUT_DIR>
Directory containing crashes found by libFuzzer [default: .]
Directory containing crashes found by libFuzzer or LibAFL [default: .]
-o, --output <OUTPUT_DIR>
Output directory with triaged reports
--join <PREV_CLUSTERS_DIR>
Expand All @@ -618,7 +619,8 @@ Triage crashes found by libFuzzer based fuzzer
[libFuzzer](https://www.llvm.org/docs/LibFuzzer.html) based fuzzers
(C/C++/[go-fuzz](https://github.com/dvyukov/go-fuzz)/[Atheris](https://github.com/google/atheris)/
[Jazzer](https://github.com/CodeIntelligenceTesting/jazzer)/[Jazzer.js](https://github.com/CodeIntelligenceTesting/jazzer.js)/
[jsfuzz](https://github.com/fuzzitdev/jsfuzz)).
[jsfuzz](https://github.com/fuzzitdev/jsfuzz)) or [LibAFL](https://github.com/AFLplusplus/LibAFL) based
[fuzzers](https://github.com/AFLplusplus/LibAFL/tree/main/fuzzers).
It is pretty much like `casr-afl`.

libFuzzer example:
Expand All @@ -644,6 +646,10 @@ Jazzer.js example (Jazzer.js installation [guide](https://github.com/CodeIntelli
$ sudo npm install --save-dev @jazzer.js/core
$ casr-libfuzzer -i ./xml2js -o casr/tests/tmp_tests_casr/xml2js_fuzzer_out/out -- npx jazzer casr/tests/tmp_tests_casr/xml2js_fuzzer_out/xml2js_fuzzer.js

LibAFL example:

$ casr-libfuzzer -i casr/tests/casr_tests/casrep/test_libafl_crashes -o casr/tests/tmp_tests_casr/casr_libafl_out -- casr/tests/casr_tests/bin/test_libafl_fuzzer @@

You can set environment variable `RUST_BACKTRACE=(1|full)` for `casr-libfuzzer`. This
variable may be used by [casr-san](#casr-san).

Expand Down

0 comments on commit dc48b45

Please sign in to comment.