From e04e640150a15362ad6c5f5740990b1989bf20f9 Mon Sep 17 00:00:00 2001 From: headshog Date: Wed, 21 Feb 2024 15:45:24 +0300 Subject: [PATCH 01/38] Casr-csharp init --- .github/workflows/aarch64.yml | 9 ++ .github/workflows/amd64.yml | 6 ++ .github/workflows/coverage.yaml | 6 ++ README.md | 13 ++- casr/src/bin/casr-afl.rs | 123 ++++++++++++++-------- casr/src/bin/casr-cli.rs | 4 + casr/src/bin/casr-cluster.rs | 2 +- casr/src/bin/casr-csharp.rs | 178 ++++++++++++++++++++++++++++++++ casr/src/triage.rs | 3 + 9 files changed, 298 insertions(+), 46 deletions(-) create mode 100644 casr/src/bin/casr-csharp.rs diff --git a/.github/workflows/aarch64.yml b/.github/workflows/aarch64.yml index 4817d35e..7616c910 100644 --- a/.github/workflows/aarch64.yml +++ b/.github/workflows/aarch64.yml @@ -26,6 +26,15 @@ jobs: export CARGO_TERM_COLOR=always export CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse apt-get update && apt-get install -y gdb pip curl python3.10-dev clang llvm build-essential + wget https://download.visualstudio.microsoft.com/download/pr/092bec24-9cad-421d-9b43-458b3a7549aa/84280dbd1eef750f9ed1625339235c22/dotnet-sdk-8.0.101-linux-arm64.tar.gz + mkdir /dotnet && tar zxf dotnet-sdk-8.0.101-linux-arm64.tar.gz -C /dotnet && rm -rf dotnet-sdk-8.0.101-linux-arm64.tar.gz + export DOTNET_ROOT=/dotnet + export PATH=$PATH:/dotnet + export DOTNET_CLI_TELEMETRY_OPTOUT=1 + export DOTNET_UPGRADEASSISTANT_TELEMETRY_OPTOUT=1 + dotnet tool install SharpFuzz.CommandLine --global + dotnet tool install minicover --global + export PATH=$PATH:/root/.dotnet/tools curl https://sh.rustup.rs -o rustup.sh && chmod +x rustup.sh && \ ./rustup.sh -y && rm rustup.sh run: | diff --git a/.github/workflows/amd64.yml b/.github/workflows/amd64.yml index 48c7fe84..c696ef63 100644 --- a/.github/workflows/amd64.yml +++ b/.github/workflows/amd64.yml @@ -29,6 +29,12 @@ jobs: sudo apt update && sudo apt install -y nodejs sudo npm install -g jsfuzz sudo npm install --save-dev @jazzer.js/core + wget -q https://packages.microsoft.com/config/ubuntu/22.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb + dpkg -i packages-microsoft-prod.deb && rm packages-microsoft-prod.deb + apt update && apt install -y --no-install-recommends dotnet-sdk-8.0 + dotnet tool install SharpFuzz.CommandLine --global + dotnet tool install minicover --global + export PATH=$PATH:/root/.dotnet/tools curl https://sh.rustup.rs -o rustup.sh && chmod +x rustup.sh && \ ./rustup.sh -y && rm rustup.sh rustup install nightly diff --git a/.github/workflows/coverage.yaml b/.github/workflows/coverage.yaml index 9d806814..ee75b225 100644 --- a/.github/workflows/coverage.yaml +++ b/.github/workflows/coverage.yaml @@ -27,6 +27,12 @@ jobs: sudo apt update && sudo apt install -y nodejs sudo npm install -g jsfuzz sudo npm install --save-dev @jazzer.js/core + wget -q https://packages.microsoft.com/config/ubuntu/22.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb + dpkg -i packages-microsoft-prod.deb && rm packages-microsoft-prod.deb + apt update && apt install -y --no-install-recommends dotnet-sdk-8.0 + dotnet tool install SharpFuzz.CommandLine --global + dotnet tool install minicover --global + export PATH=$PATH:/root/.dotnet/tools curl https://sh.rustup.rs -o rustup.sh && chmod +x rustup.sh && \ ./rustup.sh -y && rm rustup.sh rustup install nightly diff --git a/README.md b/README.md index df689455..c05a4416 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,8 @@ java reports and get report from to analyze JavaScript reports and get report from [Jazzer.js](https://github.com/CodeIntelligenceTesting/jazzer.js) or [jsfuzz](https://github.com/fuzzitdev/jsfuzz). +Use `casr-csharp` to analyze C# reports and get report from +[Sharpfuzz](https://github.com/Metalnem/sharpfuzz). Crash report contains many useful information: severity (like [exploitable](https://github.com/jfoote/exploitable)) for x86, x86\_64, arm32, aarch64, rv32g, rv64g architectures, @@ -49,7 +51,7 @@ Triage is based on stack trace comparison from [gdb-command](https://github.com/ [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))/[Sharpfuzz](https://github.com/Metalnem/sharpfuzz). `casr-dojo` allows to upload new and unique CASR reports to [DefectDojo](https://github.com/DefectDojo/django-DefectDojo) (available with `dojo` feature). @@ -81,6 +83,7 @@ and program languages: * Python * Java * JavaScript +* C# It could be built with `exploitable` feature for severity estimation crashes collected from gdb. To save crash reports as json use `serde` feature. @@ -161,6 +164,10 @@ Create report from JavaScript: $ casr-js -o js.casrep -- node casr/tests/casr_tests/js/test_casr_js.js +Create report from C#: + + $ casr-csharp -o csharp.casrep -- dotnet casr/tests/casr_tests/csharp/test_casr_csharp.cs + View report: $ casr-cli casr/tests/casr_tests/casrep/test_clustering_san/load_fuzzer_crash-120697a7f5b87c03020f321c8526adf0f4bcc2dc.casrep @@ -227,7 +234,7 @@ Upload new and unique CASR reports to When you have crashes from fuzzing you may do the following steps: 1. Create reports for all crashes via `casr-san`, `casr-gdb` (if no sanitizers - are present), `casr-python`, `casr-java`, or `casr-js`. + are present), `casr-python`, `casr-java`, `casr-js`, or `casr-csharp`. 2. Deduplicate collected crash reports via `casr-cluster -d`. 3. Cluster deduplicated crash reports via `casr-cluster -c`. 4. Create reports and deduplicate them for all UBSAN errors via `casr-ubsan`. @@ -242,7 +249,7 @@ If you use [AFL++](https://github.com/AFLplusplus/AFLplusplus), 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)), +[jsfuzz](https://github.com/fuzzitdev/jsfuzz))/[Sharpfuzz](https://github.com/Metalnem/sharpfuzz), the pipeline (without `casr-ubsan` and `casr-dojo`) could be done automatically by `casr-libfuzzer`. diff --git a/casr/src/bin/casr-afl.rs b/casr/src/bin/casr-afl.rs index 5cee25bd..208f6267 100644 --- a/casr/src/bin/casr-afl.rs +++ b/casr/src/bin/casr-afl.rs @@ -1,7 +1,7 @@ use casr::triage::{fuzzing_crash_triage_pipeline, CrashInfo}; use casr::util; -use anyhow::{bail, Result}; +use anyhow::Result; use clap::{ error::{ContextKind, ContextValue, ErrorKind}, Arg, ArgAction, @@ -15,7 +15,7 @@ use std::path::{Path, PathBuf}; fn main() -> Result<()> { let matches = clap::Command::new("casr-afl") .version(clap::crate_version!()) - .about("Triage crashes found by AFL++") + .about("Triage crashes found by AFL++)") .term_width(90) .arg( Arg::new("log-level") @@ -86,7 +86,7 @@ fn main() -> Result<()> { Arg::new("ignore-cmdline") .action(ArgAction::SetTrue) .long("ignore-cmdline") - .help("Force usage to run target instead of searching for cmdline files in AFL fuzzing directory") + .help("Force usage to run target instead of searching for cmdline files in AFL fuzzing directory") ) .arg( Arg::new("no-cluster") @@ -94,31 +94,44 @@ fn main() -> Result<()> { .long("no-cluster") .help("Do not cluster CASR reports") ) + .arg( + Arg::new("casr-gdb-args") + .long("casr-gdb-args") + .action(ArgAction::Set) + .help("Add \"--casr-gdb-args \'./gdb_fuzz_target \'\" to generate additional crash reports with casr-gdb (e.g., test whether program crashes without sanitizers)"), + ) .arg( Arg::new("ARGS") .action(ArgAction::Set) .required(false) .num_args(1..) .last(true) - .help("Add \"-- ./gdb_fuzz_target \" to generate additional crash reports with casr-gdb (e.g., test whether program crashes without sanitizers)"), + .help("Add \"-- fuzz_target (for C#)\""), ) .get_matches(); // Init log. util::initialize_logging(&matches); - let casr_san = util::get_path("casr-san")?; - let casr_gdb = util::get_path("casr-gdb")?; - - let mut gdb_args = if let Some(argv) = matches.get_many::("ARGS") { - argv.cloned().collect() + // Get gdb args. + let mut gdb_args = if let Some(argv) = matches.get_one::("casr-gdb-args") { + shell_words::split(argv)? } else { Vec::new() }; - if gdb_args.is_empty() && matches.get_flag("ignore-cmdline") { - bail!("ARGS is empty, but \"ignore-cmdline\" option is provided."); - } + // Get fuzz target args (for C#). + let mut argv: Vec<&str> = if let Some(argvs) = matches.get_many::("ARGS") { + argvs.map(|v| v.as_str()).collect() + } else { + Vec::new() + }; + let at_index = if let Some(idx) = argv.iter().skip(1).position(|s| s.contains("@@")) { + idx + 1 + } else { + argv.push("@@"); + argv.len() - 1 + }; // Get all crashes. let mut crashes: HashMap = HashMap::new(); @@ -129,44 +142,70 @@ fn main() -> Result<()> { } // Get crashes from one node. - let mut crash_info = casr::triage::CrashInfo { - casr_tool: casr_gdb.clone(), - ..Default::default() - }; - crash_info.target_args = if matches.get_flag("ignore-cmdline") { - gdb_args.clone() + let mut crash_info = if !gdb_args.is_empty() { + CrashInfo { + target_args: if matches.get_flag("ignore-cmdline") { + gdb_args.clone() + } else { + let cmdline_path = path.join("cmdline"); + if let Ok(cmdline) = fs::read_to_string(&cmdline_path) { + cmdline.split_whitespace().map(|s| s.to_string()).collect() + } else { + error!("Couldn't read {}.", cmdline_path.display()); + continue; + } + }, + envs: HashMap::new(), + casr_tool: util::get_path("casr-gdb")?, + ..Default::default() + } } else { - let cmdline_path = path.join("cmdline"); - if let Ok(cmdline) = fs::read_to_string(&cmdline_path) { - cmdline.split_whitespace().map(|s| s.to_string()).collect() - } else { - error!("Couldn't read {}.", cmdline_path.display()); - continue; + CrashInfo { + target_args: argv.iter().map(|x| x.to_string()).collect(), + envs: vec![ + ("AFL_SKIP_BIN_CHECK".to_string(), "1".to_string()), + ( + "AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES".to_string(), + "1".to_string(), + ), + ] + .into_iter() + .collect(), + at_index: Some(at_index), + casr_tool: util::get_path("casr-csharp")?, + ..Default::default() } }; - crash_info.at_index = crash_info - .target_args - .iter() - .skip(1) - .position(|s| s.contains("@@")) - .map(|x| x + 1); - if let Some(target) = crash_info.target_args.first() { - match util::symbols_list(Path::new(target)) { - Ok(list) => { - if list.contains("__asan") { - crash_info.casr_tool = casr_san.clone() + // When we triage crashes for binaries, use casr-san. + if !gdb_args.is_empty() { + let casr_san = util::get_path("casr-san")?; + + crash_info.at_index = crash_info + .target_args + .iter() + .skip(1) + .position(|s| s.contains("@@")) + .map(|x| x + 1); + + if let Some(target) = crash_info.target_args.first() { + match util::symbols_list(Path::new(target)) { + Ok(list) => { + if list.contains("__asan") { + crash_info.casr_tool = casr_san.clone() + } + } + Err(e) => { + error!("{e}"); + continue; } } - Err(e) => { - error!("{e}"); - continue; - } + } else { + error!("Cmdline is empty. Path: {:?}", path.join("cmdline")); + continue; } - } else { - error!("Cmdline is empty. Path: {:?}", path.join("cmdline")); - continue; } + // Push crash paths. for crash in path .read_dir()? diff --git a/casr/src/bin/casr-cli.rs b/casr/src/bin/casr-cli.rs index 4229804f..34b863b8 100644 --- a/casr/src/bin/casr-cli.rs +++ b/casr/src/bin/casr-cli.rs @@ -665,6 +665,10 @@ fn build_slider_report( select.add_item("JsReport", report.js_report.join("\n")); } + if !report.csharp_report.is_empty() { + select.add_item("CsharpReport", report.csharp_report.join("\n")); + } + if !report.csharp_report.is_empty() { select.add_item("CSharpReport", report.csharp_report.join("\n")); } diff --git a/casr/src/bin/casr-cluster.rs b/casr/src/bin/casr-cluster.rs index 83971af2..ae465a23 100644 --- a/casr/src/bin/casr-cluster.rs +++ b/casr/src/bin/casr-cluster.rs @@ -694,7 +694,7 @@ fn main() -> Result<()> { .value_parser(clap::value_parser!(u32).range(1..)) ) .get_matches(); - init_ignored_frames!("cpp", "rust", "python", "go", "java", "js"); + init_ignored_frames!("cpp", "rust", "python", "go", "java", "js", "csharp"); // Get number of threads let jobs = if let Some(jobs) = matches.get_one::("jobs") { diff --git a/casr/src/bin/casr-csharp.rs b/casr/src/bin/casr-csharp.rs new file mode 100644 index 00000000..18849efc --- /dev/null +++ b/casr/src/bin/casr-csharp.rs @@ -0,0 +1,178 @@ +use casr::util; +use libcasr::{ + exception::Exception, init_ignored_frames, java::*, report::CrashReport, stacktrace::*, +}; + +use anyhow::{bail, Result}; +use clap::{Arg, ArgAction, ArgGroup}; +use regex::Regex; +use std::path::PathBuf; +use std::process::Command; +use walkdir::WalkDir; + +fn main() -> Result<()> { + let matches = clap::Command::new("casr-csharp") + .version(clap::crate_version!()) + .about("Create CASR reports (.casrep) from C# reports") + .term_width(90) + .arg( + Arg::new("output") + .short('o') + .long("output") + .action(ArgAction::Set) + .value_parser(clap::value_parser!(PathBuf)) + .value_name("REPORT") + .help( + "Path to save report. Path can be a directory, then report name is generated", + ), + ) + .arg( + Arg::new("stdout") + .action(ArgAction::SetTrue) + .long("stdout") + .help("Print CASR report to stdout"), + ) + .group( + ArgGroup::new("out") + .args(["stdout", "output"]) + .required(true), + ) + .arg( + Arg::new("stdin") + .long("stdin") + .action(ArgAction::Set) + .value_parser(clap::value_parser!(PathBuf)) + .value_name("FILE") + .help("Stdin file for program"), + ) + // This is maybe not needed. + .arg( + Arg::new("source-dirs") + .long("source-dirs") + .env("CASR_SOURCE_DIRS") + .action(ArgAction::Set) + .num_args(1..) + .value_delimiter(':') + .value_parser(clap::value_parser!(PathBuf)) + .value_name("DIR") + .help("Paths to directories with Java source files (list separated by ':' for env)"), + ) + .arg( + Arg::new("timeout") + .short('t') + .long("timeout") + .action(ArgAction::Set) + .default_value("0") + .value_name("SECONDS") + .help("Timeout (in seconds) for target execution, 0 value means that timeout is disabled") + .value_parser(clap::value_parser!(u64).range(0..)) + ) + .arg( + Arg::new("ignore") + .long("ignore") + .action(ArgAction::Set) + .value_parser(clap::value_parser!(PathBuf)) + .value_name("FILE") + .help("File with regular expressions for functions and file paths that should be ignored"), + ) + // This is maybe not needed. + .arg( + Arg::new("strip-path") + .long("strip-path") + .env("CASR_STRIP_PATH") + .action(ArgAction::Set) + .value_name("PREFIX") + .help("Path prefix to strip from stacktrace and crash line"), + ) + // Maybe something else is needed to be added here. + .arg( + Arg::new("ARGS") + .action(ArgAction::Set) + .num_args(1..) + .last(true) + .help("Add \"-- \" to run"), + ) + .get_matches(); + + // Maybe here is not "csharp", but "cs". + init_ignored_frames!("csharp", "cpp"); //TODO + if let Some(path) = matches.get_one::("ignore") { + util::add_custom_ignored_frames(path)?; + } + // Get program args. + let argv: Vec<&str> = if let Some(argvs) = matches.get_many::("ARGS") { + argvs.map(|s| s.as_str()).collect() + } else { + bail!("Wrong arguments for starting program"); + }; + + // Get stdin for target program. + let stdin_file = util::stdin_from_matches(&matches)?; + + // Get timeout + let timeout = *matches.get_one::("timeout").unwrap(); + + // Run program. + let mut csharp_cmd = Command::new(argv[0]); + if let Some(ref file) = stdin_file { + csharp_cmd.stdin(std::fs::File::open(file)?); + } + if argv.len() > 1 { + csharp_cmd.args(&argv[1..]); + } + let csharp_result = util::get_output(&mut csharp_cmd, timeout, true)?; + + let csharp_stderr = String::from_utf8_lossy(&csharp_result.stderr); + + // Create report. + let mut report = CrashReport::new(); + // Set executable path (java class path) + // + // Here is the special code for csharp. + // + report.proc_cmdline = argv.join(" "); + let _ = report.add_os_info(); + let _ = report.add_proc_environ(); + + // Get C# report. + let csharp_stderr_list: Vec = + csharp_stderr.split('\n').map(|l| l.to_string()).collect(); + let re = Regex::new(r"Exception in thread .*? |== Java Exception: ").unwrap(); + // + // Here is the special regex for csharp. + // + if let Some(start) = csharp_stderr_list.iter().position(|x| re.is_match(x)) { + report.csharp_report = csharp_stderr_list[start..].to_vec(); + // + // Parsing of stderr list here. + // + //let report_str = report.csharp_report.join("\n"); + //report.stacktrace = CSharpStacktrace::extract_stacktrace(&report_str)?; + //if let Some(exception) = CSharpException::parse_exception(&report_str) { + // report.execution_class = exception; + //} + } else { + // Call error here maybe. + } + + let stacktrace = JavaStacktrace::parse_stacktrace(&report.stacktrace)?; + //let stacktrace = CSharpStacktrace::parse_stacktrace(&report.stacktrace)?; + if let Ok(crash_line) = stacktrace.crash_line() { + report.crashline = crash_line.to_string(); + if let CrashLine::Source(debug) = crash_line { + // + // Maybe other code is here. + // + if let Some(sources) = CrashReport::sources(&debug) { + report.source = sources; + } + } + } + + // + // Something else is here maybe. + // + + //Output report + util::output_report(&report, &matches, &argv) +} diff --git a/casr/src/triage.rs b/casr/src/triage.rs index 1dd4fd31..4df05b71 100644 --- a/casr/src/triage.rs +++ b/casr/src/triage.rs @@ -63,6 +63,9 @@ impl<'a> CrashInfo { if tool_name.eq("casr-python") { args.push("python3".to_string()); } + if tool_name.eq("casr-csharp") { + // Maybe push some additional args here. + } let offset = args.len(); args.extend_from_slice(&self.target_args); if let Some(at_index) = self.at_index { From 22e5f9db9ede46156d39590cc8832ae23fdd8f93 Mon Sep 17 00:00:00 2001 From: headshog Date: Wed, 6 Mar 2024 16:09:46 +0300 Subject: [PATCH 02/38] casr-csharp fix --- README.md | 7 ++++--- casr/src/bin/casr-cli.rs | 2 +- casr/src/bin/casr-csharp.rs | 19 +++++++++---------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index c05a4416..58022958 100644 --- a/README.md +++ b/README.md @@ -166,7 +166,7 @@ Create report from JavaScript: Create report from C#: - $ casr-csharp -o csharp.casrep -- dotnet casr/tests/casr_tests/csharp/test_casr_csharp.cs + $ casr-csharp -o csharp.casrep -- dotnet casr/tests/casr_tests/csharp/test_casr_csharp.csproj View report: @@ -249,10 +249,11 @@ If you use [AFL++](https://github.com/AFLplusplus/AFLplusplus), 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))/[Sharpfuzz](https://github.com/Metalnem/sharpfuzz), -the pipeline (without `casr-ubsan` and `casr-dojo`) could be done automatically +[jsfuzz](https://github.com/fuzzitdev/jsfuzz)), the pipeline (without `casr-ubsan` and `casr-dojo`) could be done automatically by `casr-libfuzzer`. +If you use [Sharpfuzz](https://www.llvm.org/docs/LibFuzzer.html), the pipeline (without `casr-ubsan` and `casr-dojo`) could be done automatically by `casr-afl`. + ## Contributing Feel free to open [issues](https://github.com/ispras/casr/issues) or [PRs](https://github.com/ispras/casr/pulls) (especially pay attention to [help wanted](https://github.com/ispras/casr/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22) issues)! We appreciate your support! diff --git a/casr/src/bin/casr-cli.rs b/casr/src/bin/casr-cli.rs index 34b863b8..9a2bbd4d 100644 --- a/casr/src/bin/casr-cli.rs +++ b/casr/src/bin/casr-cli.rs @@ -666,7 +666,7 @@ fn build_slider_report( } if !report.csharp_report.is_empty() { - select.add_item("CsharpReport", report.csharp_report.join("\n")); + select.add_item("CSharpReport", report.csharp_report.join("\n")); } if !report.csharp_report.is_empty() { diff --git a/casr/src/bin/casr-csharp.rs b/casr/src/bin/casr-csharp.rs index 18849efc..6e242fd9 100644 --- a/casr/src/bin/casr-csharp.rs +++ b/casr/src/bin/casr-csharp.rs @@ -1,6 +1,7 @@ use casr::util; use libcasr::{ - exception::Exception, init_ignored_frames, java::*, report::CrashReport, stacktrace::*, + csharp::*, exception::Exception, init_ignored_frames, java::*, report::CrashReport, + stacktrace::*, }; use anyhow::{bail, Result}; @@ -126,9 +127,8 @@ fn main() -> Result<()> { // Create report. let mut report = CrashReport::new(); - // Set executable path (java class path) // - // Here is the special code for csharp. + // Here is the special code for csharp that creates report. // report.proc_cmdline = argv.join(" "); let _ = report.add_os_info(); @@ -146,17 +146,16 @@ fn main() -> Result<()> { // // Parsing of stderr list here. // - //let report_str = report.csharp_report.join("\n"); - //report.stacktrace = CSharpStacktrace::extract_stacktrace(&report_str)?; - //if let Some(exception) = CSharpException::parse_exception(&report_str) { - // report.execution_class = exception; - //} + let report_str = report.csharp_report.join("\n"); + report.stacktrace = CSharpStacktrace::extract_stacktrace(&report_str)?; + if let Some(exception) = CSharpException::parse_exception(&report_str) { + report.execution_class = exception; + } } else { // Call error here maybe. } - let stacktrace = JavaStacktrace::parse_stacktrace(&report.stacktrace)?; - //let stacktrace = CSharpStacktrace::parse_stacktrace(&report.stacktrace)?; + let stacktrace = CSharpStacktrace::parse_stacktrace(&report.stacktrace)?; if let Ok(crash_line) = stacktrace.crash_line() { report.crashline = crash_line.to_string(); if let CrashLine::Source(debug) = crash_line { From f25a74240cd4a5410d5577df2557e26883731d0a Mon Sep 17 00:00:00 2001 From: headshog Date: Wed, 6 Mar 2024 18:19:45 +0300 Subject: [PATCH 03/38] casr-csharp fix x2 --- README.md | 2 +- casr/src/bin/casr-afl.rs | 53 +++++++++++++++---------------- casr/src/bin/casr-csharp.rs | 57 ++++++---------------------------- casr/src/bin/casr-libfuzzer.rs | 12 ++++--- 4 files changed, 45 insertions(+), 79 deletions(-) diff --git a/README.md b/README.md index 58022958..6e3d8dd7 100644 --- a/README.md +++ b/README.md @@ -166,7 +166,7 @@ Create report from JavaScript: Create report from C#: - $ casr-csharp -o csharp.casrep -- dotnet casr/tests/casr_tests/csharp/test_casr_csharp.csproj + $ casr-csharp -o csharp.casrep -- dotnet casr/tests/casr_tests/csharp/test_casr_csharp.dll View report: diff --git a/casr/src/bin/casr-afl.rs b/casr/src/bin/casr-afl.rs index 208f6267..152b0d31 100644 --- a/casr/src/bin/casr-afl.rs +++ b/casr/src/bin/casr-afl.rs @@ -103,7 +103,6 @@ fn main() -> Result<()> { .arg( Arg::new("ARGS") .action(ArgAction::Set) - .required(false) .num_args(1..) .last(true) .help("Add \"-- fuzz_target (for C#)\""), @@ -113,13 +112,6 @@ fn main() -> Result<()> { // Init log. util::initialize_logging(&matches); - // Get gdb args. - let mut gdb_args = if let Some(argv) = matches.get_one::("casr-gdb-args") { - shell_words::split(argv)? - } else { - Vec::new() - }; - // Get fuzz target args (for C#). let mut argv: Vec<&str> = if let Some(argvs) = matches.get_many::("ARGS") { argvs.map(|v| v.as_str()).collect() @@ -133,6 +125,29 @@ fn main() -> Result<()> { argv.len() - 1 }; + // Get tool. + let tool = if !argv.is_empty() && argv[0].ends_with(".dll") { + "casr-csharp" + } else { + let sym_list = util::symbols_list(Path::new(argv[0]))?; + if sym_list.contains("__asan") { + "casr-san" + } else { + "casr-gdb" + } + }; + let tool_path = util::get_path(tool)?; + + // Get gdb args. + let argv = matches.get_one::("casr-gdb-args"); + let gdb_args = if (tool != "casr-gdb" && tool != "casr-san" && argv.is_none()) + || matches.get_flag("ignore-cmdline") + { + Vec::new() + } else { + shell_words::split(argv.unwrap())? + }; + // Get all crashes. let mut crashes: HashMap = HashMap::new(); for node_dir in fs::read_dir(matches.get_one::("input").unwrap())? { @@ -156,7 +171,6 @@ fn main() -> Result<()> { } }, envs: HashMap::new(), - casr_tool: util::get_path("casr-gdb")?, ..Default::default() } } else { @@ -172,15 +186,13 @@ fn main() -> Result<()> { .into_iter() .collect(), at_index: Some(at_index), - casr_tool: util::get_path("casr-csharp")?, ..Default::default() } }; + crash_info.casr_tool = tool_path.clone(); // When we triage crashes for binaries, use casr-san. if !gdb_args.is_empty() { - let casr_san = util::get_path("casr-san")?; - crash_info.at_index = crash_info .target_args .iter() @@ -189,16 +201,9 @@ fn main() -> Result<()> { .map(|x| x + 1); if let Some(target) = crash_info.target_args.first() { - match util::symbols_list(Path::new(target)) { - Ok(list) => { - if list.contains("__asan") { - crash_info.casr_tool = casr_san.clone() - } - } - Err(e) => { - error!("{e}"); - continue; - } + if let Err(e) = util::symbols_list(Path::new(target)) { + error!("{e}"); + continue; } } else { error!("Cmdline is empty. Path: {:?}", path.join("cmdline")); @@ -222,10 +227,6 @@ fn main() -> Result<()> { } } - if matches.get_flag("ignore-cmdline") { - gdb_args = Vec::new(); - } - // Generate reports fuzzing_crash_triage_pipeline(&matches, &crashes, &gdb_args) } diff --git a/casr/src/bin/casr-csharp.rs b/casr/src/bin/casr-csharp.rs index 6e242fd9..0bdc621c 100644 --- a/casr/src/bin/casr-csharp.rs +++ b/casr/src/bin/casr-csharp.rs @@ -1,7 +1,6 @@ use casr::util; use libcasr::{ - csharp::*, exception::Exception, init_ignored_frames, java::*, report::CrashReport, - stacktrace::*, + csharp::*, exception::Exception, init_ignored_frames, report::CrashReport, stacktrace::*, }; use anyhow::{bail, Result}; @@ -9,7 +8,6 @@ use clap::{Arg, ArgAction, ArgGroup}; use regex::Regex; use std::path::PathBuf; use std::process::Command; -use walkdir::WalkDir; fn main() -> Result<()> { let matches = clap::Command::new("casr-csharp") @@ -46,18 +44,6 @@ fn main() -> Result<()> { .value_name("FILE") .help("Stdin file for program"), ) - // This is maybe not needed. - .arg( - Arg::new("source-dirs") - .long("source-dirs") - .env("CASR_SOURCE_DIRS") - .action(ArgAction::Set) - .num_args(1..) - .value_delimiter(':') - .value_parser(clap::value_parser!(PathBuf)) - .value_name("DIR") - .help("Paths to directories with Java source files (list separated by ':' for env)"), - ) .arg( Arg::new("timeout") .short('t') @@ -76,16 +62,6 @@ fn main() -> Result<()> { .value_name("FILE") .help("File with regular expressions for functions and file paths that should be ignored"), ) - // This is maybe not needed. - .arg( - Arg::new("strip-path") - .long("strip-path") - .env("CASR_STRIP_PATH") - .action(ArgAction::Set) - .value_name("PREFIX") - .help("Path prefix to strip from stacktrace and crash line"), - ) - // Maybe something else is needed to be added here. .arg( Arg::new("ARGS") .action(ArgAction::Set) @@ -95,7 +71,6 @@ fn main() -> Result<()> { ) .get_matches(); - // Maybe here is not "csharp", but "cs". init_ignored_frames!("csharp", "cpp"); //TODO if let Some(path) = matches.get_one::("ignore") { util::add_custom_ignored_frames(path)?; @@ -127,9 +102,13 @@ fn main() -> Result<()> { // Create report. let mut report = CrashReport::new(); - // - // Here is the special code for csharp that creates report. - // + // Set executable path (for C# .dll file) + if let Some(pos) = argv.iter().position(|x| x.ends_with(".dll")) { + let Some(classes) = argv.get(pos + 1) else { + bail!("dotnet target is not specified by .dll executable."); + }; + report.executable_path = classes.to_string(); + } report.proc_cmdline = argv.join(" "); let _ = report.add_os_info(); let _ = report.add_proc_environ(); @@ -137,41 +116,25 @@ fn main() -> Result<()> { // Get C# report. let csharp_stderr_list: Vec = csharp_stderr.split('\n').map(|l| l.to_string()).collect(); - let re = Regex::new(r"Exception in thread .*? |== Java Exception: ").unwrap(); - // - // Here is the special regex for csharp. - // + let re = Regex::new(r"(?m)^Unhandled [Ee]xception(?::\n|\. )(?:.|\n)*?((?:[ \n\t]*(?:at [\S ]+|--- End of inner exception stack trace ---))+)$").unwrap(); if let Some(start) = csharp_stderr_list.iter().position(|x| re.is_match(x)) { report.csharp_report = csharp_stderr_list[start..].to_vec(); - // - // Parsing of stderr list here. - // let report_str = report.csharp_report.join("\n"); report.stacktrace = CSharpStacktrace::extract_stacktrace(&report_str)?; if let Some(exception) = CSharpException::parse_exception(&report_str) { report.execution_class = exception; } - } else { - // Call error here maybe. } - let stacktrace = CSharpStacktrace::parse_stacktrace(&report.stacktrace)?; - if let Ok(crash_line) = stacktrace.crash_line() { + if let Ok(crash_line) = CSharpStacktrace::parse_stacktrace(&report.stacktrace)?.crash_line() { report.crashline = crash_line.to_string(); if let CrashLine::Source(debug) = crash_line { - // - // Maybe other code is here. - // if let Some(sources) = CrashReport::sources(&debug) { report.source = sources; } } } - // - // Something else is here maybe. - // - //Output report util::output_report(&report, &matches, &argv) } diff --git a/casr/src/bin/casr-libfuzzer.rs b/casr/src/bin/casr-libfuzzer.rs index 76b970c2..ae2aa152 100644 --- a/casr/src/bin/casr-libfuzzer.rs +++ b/casr/src/bin/casr-libfuzzer.rs @@ -142,7 +142,7 @@ fn main() -> Result<()> { "casr-gdb" } }; - let tool = util::get_path(tool)?; + let tool_path = util::get_path(tool)?; // Get all crashes. let crashes: HashMap = fs::read_dir(input_dir)? @@ -159,16 +159,18 @@ fn main() -> Result<()> { target_args: argv.iter().map(|x| x.to_string()).collect(), envs: envs.clone(), at_index: Some(at_index), - casr_tool: tool.clone(), + casr_tool: tool_path.clone(), }, ) }) .collect(); - let gdb_args = if let Some(argv) = matches.get_one::("casr-gdb-args") { - shell_words::split(argv)? - } else { + let gdb_args: Vec; + let argv = matches.get_one::("casr-gdb-args"); + gdb_args = if tool != "casr-gdb" && tool != "casr-san" && argv.is_none() { Vec::new() + } else { + shell_words::split(argv.unwrap())? }; // Generate reports From a693fb3854fef1409af7857e749bfeab6bf7617b Mon Sep 17 00:00:00 2001 From: headshog Date: Wed, 6 Mar 2024 22:01:06 +0300 Subject: [PATCH 04/38] casr-afl fix --- README.md | 5 +- casr/src/bin/casr-afl.rs | 12 ++--- casr/src/bin/casr-csharp.rs | 5 +- casr/src/bin/casr-libfuzzer.rs | 3 +- docs/usage.md | 87 ++++++++++++++++++++++++++-------- 5 files changed, 80 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 6e3d8dd7..82b272d3 100644 --- a/README.md +++ b/README.md @@ -46,12 +46,13 @@ stored in JSON format. `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`. Triage is based on stack trace comparison from [gdb-command](https://github.com/anfedotoff/gdb-command). -`casr-afl` is used to triage crashes found by [AFL++](https://github.com/AFLplusplus/AFLplusplus). +`casr-afl` is used to triage 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) 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))/[Sharpfuzz](https://github.com/Metalnem/sharpfuzz). +[jsfuzz](https://github.com/fuzzitdev/jsfuzz)). `casr-dojo` allows to upload new and unique CASR reports to [DefectDojo](https://github.com/DefectDojo/django-DefectDojo) (available with `dojo` feature). diff --git a/casr/src/bin/casr-afl.rs b/casr/src/bin/casr-afl.rs index 152b0d31..bc6c50a1 100644 --- a/casr/src/bin/casr-afl.rs +++ b/casr/src/bin/casr-afl.rs @@ -15,7 +15,7 @@ use std::path::{Path, PathBuf}; fn main() -> Result<()> { let matches = clap::Command::new("casr-afl") .version(clap::crate_version!()) - .about("Triage crashes found by AFL++)") + .about("Triage crashes found by AFL++ (Sharpfuzz)") .term_width(90) .arg( Arg::new("log-level") @@ -105,7 +105,7 @@ fn main() -> Result<()> { .action(ArgAction::Set) .num_args(1..) .last(true) - .help("Add \"-- fuzz_target (for C#)\""), + .help("Add \"-- fuzz_target \""), ) .get_matches(); @@ -126,7 +126,7 @@ fn main() -> Result<()> { }; // Get tool. - let tool = if !argv.is_empty() && argv[0].ends_with(".dll") { + let tool = if let Some(_) = argv.iter().position(|x| x.ends_with(".dll")) { "casr-csharp" } else { let sym_list = util::symbols_list(Path::new(argv[0]))?; @@ -139,13 +139,13 @@ fn main() -> Result<()> { let tool_path = util::get_path(tool)?; // Get gdb args. - let argv = matches.get_one::("casr-gdb-args"); - let gdb_args = if (tool != "casr-gdb" && tool != "casr-san" && argv.is_none()) + let gdb_argv = matches.get_one::("casr-gdb-args"); + let gdb_args = if (tool != "casr-gdb" && tool != "casr-san" && gdb_argv.is_none()) || matches.get_flag("ignore-cmdline") { Vec::new() } else { - shell_words::split(argv.unwrap())? + shell_words::split(gdb_argv.unwrap())? }; // Get all crashes. diff --git a/casr/src/bin/casr-csharp.rs b/casr/src/bin/casr-csharp.rs index 0bdc621c..59523cee 100644 --- a/casr/src/bin/casr-csharp.rs +++ b/casr/src/bin/casr-csharp.rs @@ -104,7 +104,7 @@ fn main() -> Result<()> { let mut report = CrashReport::new(); // Set executable path (for C# .dll file) if let Some(pos) = argv.iter().position(|x| x.ends_with(".dll")) { - let Some(classes) = argv.get(pos + 1) else { + let Some(classes) = argv.get(pos) else { bail!("dotnet target is not specified by .dll executable."); }; report.executable_path = classes.to_string(); @@ -116,7 +116,8 @@ fn main() -> Result<()> { // Get C# report. let csharp_stderr_list: Vec = csharp_stderr.split('\n').map(|l| l.to_string()).collect(); - let re = Regex::new(r"(?m)^Unhandled [Ee]xception(?::\n|\. )(?:.|\n)*?((?:[ \n\t]*(?:at [\S ]+|--- End of inner exception stack trace ---))+)$").unwrap(); + //let re = Regex::new(r"(?m)^Unhandled [Ee]xception(?::\n|\. )(?:.|\n)*?((?:[ \n\t]*(?:at [\S ]+|--- End of inner exception stack trace ---))+)$").unwrap(); + let re = Regex::new(r"(?m)^Unhandled [Ee]xception(?::\n|\. )(?:.|\n)*?((?:[ \n\t]*)+)$").unwrap(); if let Some(start) = csharp_stderr_list.iter().position(|x| re.is_match(x)) { report.csharp_report = csharp_stderr_list[start..].to_vec(); let report_str = report.csharp_report.join("\n"); diff --git a/casr/src/bin/casr-libfuzzer.rs b/casr/src/bin/casr-libfuzzer.rs index ae2aa152..607d6736 100644 --- a/casr/src/bin/casr-libfuzzer.rs +++ b/casr/src/bin/casr-libfuzzer.rs @@ -165,9 +165,8 @@ fn main() -> Result<()> { }) .collect(); - let gdb_args: Vec; let argv = matches.get_one::("casr-gdb-args"); - gdb_args = if tool != "casr-gdb" && tool != "casr-san" && argv.is_none() { + let gdb_args = if tool != "casr-gdb" && tool != "casr-san" && argv.is_none() { Vec::new() } else { shell_words::split(argv.unwrap())? diff --git a/docs/usage.md b/docs/usage.md index 9c04cad7..d0cd1b61 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -9,8 +9,10 @@ java reports and get report from [Jazzer](https://github.com/CodeIntelligenceTesting/jazzer). Use `casr-js` to analyze JavaScript reports and get report from [Jazzer.js](https://github.com/CodeIntelligenceTesting/jazzer.js) or -[jsfuzz](https://github.com/fuzzitdev/jsfuzz). `casr-afl` is used -to triage crashes found by [AFL++](https://github.com/AFLplusplus/AFLplusplus). +[jsfuzz](https://github.com/fuzzitdev/jsfuzz). +Use `casr-csharp` to analyze C# reports and get report from +[Sharpfuzz](https://github.com/Metalnem/sharpfuzz). `casr-afl` can triage crashes +found by [AFL++](https://github.com/AFLplusplus/AFLplusplus) (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 @@ -202,6 +204,31 @@ Run casr-js: $ casr-js -o js.casrep -- node casr/tests/casr_tests/js/test_casr_js.js +## casr-csharp + +Create CASR reports (.casrep) from C# reports + +Usage: casr-csharp [OPTIONS] <--stdout|--output > [-- ...] + +Arguments: + [ARGS]... Add "-- " to run + +Options: + -o, --output Path to save report. Path can be a directory, then report name + is generated + --stdout Print CASR report to stdout + --stdin Stdin file for program + -t, --timeout Timeout (in seconds) for target execution, 0 value means that + timeout is disabled [default: 0] + --ignore File with regular expressions for functions and file paths that + should be ignored + -h, --help Print help + -V, --version Print version + +Run casr-csharp: + + $ casr-csharp -o csharp.casrep -- dotnet casr/tests/casr_tests/csharp/test_casr_csharp.dll + ## casr-core Analyze coredump for security goals and provide detailed report with severity estimation @@ -408,7 +435,7 @@ Convert reports to SARIF report: ## casr-afl -Triage crashes found by AFL++ +Triage crashes found by AFL++ (Sharpfuzz) Usage: casr-afl [OPTIONS] --input --output [-- ...] @@ -417,31 +444,46 @@ Triage crashes found by AFL++ with casr-gdb (e.g., test whether program crashes without sanitizers) Options: - -l, --log-level Logging level [default: info] [possible values: info, - debug] - -j, --jobs Number of parallel jobs for generating CASR reports - [default: half of cpu cores] - -t, --timeout Timeout (in seconds) for target execution, 0 value means - that timeout is disabled [default: 0] - -i, --input AFL++ work directory - -o, --output Output directory with triaged reports - -f, --force-remove Remove output project directory if it exists - --ignore-cmdline Force usage to run target instead of searching for - cmdline files in AFL fuzzing directory - --no-cluster Do not cluster CASR reports - -h, --help Print help - -V, --version Print version + -l, --log-level + Logging level [default: info] [possible values: info, debug] + -j, --jobs + Number of parallel jobs for generating CASR reports [default: half of cpu cores] + -t, --timeout + Timeout (in seconds) for target execution, 0 value means that timeout is + disabled [default: 0] + -i, --input + AFL++ work directory + -o, --output + Output directory with triaged reports + -f, --force-remove + Remove output project directory if it exists + --ignore-cmdline + Force usage to run target instead of searching for cmdline files + in AFL fuzzing directory + --no-cluster + Do not cluster CASR reports + --casr-gdb-args + Add "--casr-gdb-args './gdb_fuzz_target '" to generate additional + crash reports with casr-gdb (e.g., test whether program crashes without + sanitizers) + -h, --help + Print help + -V, --version + Print version `casr-afl` provides a straightforward CASR integration with AFL++. While walking through afl instances, `casr-afl` generates crash reports depending on target binary. For binary with ASAN `casr-san` is used, otherwise `casr-gdb`. On the next step report deduplication is done by `casr-cluster`. Finally, reports are traiged into -clusters. Crash reports contain many useful information: severity (like [exploitable](https://github.com/jfoote/exploitable)), OS and package versions, command line, stack trace, register values, -disassembly, and even source code fragment where crash appeared. +clusters. Crash reports contain many useful information: severity +(like [exploitable](https://github.com/jfoote/exploitable)), OS and package +versions, command line, stack trace, register values, disassembly, and even +source code fragment where crash appeared. `casr-afl` also provides integration with AFL-based +fuzzer [Sharpfuzz](https://github.com/Metalnem/sharpfuzz). **NOTE:** `casr-gdb` and `casr-san` should be in PATH to make `casr-afl` work. -Example (Ubuntu 20.04+): +AFL++ Example (Ubuntu 20.04+): $ cp casr/tests/casr_tests/bin/load_afl /tmp/load_afl $ cp casr/tests/casr_tests/bin/load_sydr /tmp/load_sydr @@ -516,6 +558,11 @@ you can estimate crash severity for program built without sanitizers. You can set environment variable `RUST_BACKTRACE=(1|full)` for `casr-afl`. This variable may be used by [casr-san](#casr-san). +Sharpfuzz example: + + $ ... + $ ... + ## casr-libfuzzer Triage crashes found by libFuzzer based fuzzer From a0eb1edaa46c64f36065a5b75ec4ca9a97017efb Mon Sep 17 00:00:00 2001 From: headshog Date: Wed, 6 Mar 2024 22:11:23 +0300 Subject: [PATCH 05/38] casr-afl fix x2 --- casr/src/triage.rs | 3 --- docs/usage.md | 32 ++++++++++++++++---------------- libcasr/src/report.rs | 6 ------ 3 files changed, 16 insertions(+), 25 deletions(-) diff --git a/casr/src/triage.rs b/casr/src/triage.rs index 4df05b71..1dd4fd31 100644 --- a/casr/src/triage.rs +++ b/casr/src/triage.rs @@ -63,9 +63,6 @@ impl<'a> CrashInfo { if tool_name.eq("casr-python") { args.push("python3".to_string()); } - if tool_name.eq("casr-csharp") { - // Maybe push some additional args here. - } let offset = args.len(); args.extend_from_slice(&self.target_args); if let Some(at_index) = self.at_index { diff --git a/docs/usage.md b/docs/usage.md index d0cd1b61..e3af806a 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -208,22 +208,22 @@ Run casr-js: Create CASR reports (.casrep) from C# reports -Usage: casr-csharp [OPTIONS] <--stdout|--output > [-- ...] - -Arguments: - [ARGS]... Add "-- " to run - -Options: - -o, --output Path to save report. Path can be a directory, then report name - is generated - --stdout Print CASR report to stdout - --stdin Stdin file for program - -t, --timeout Timeout (in seconds) for target execution, 0 value means that - timeout is disabled [default: 0] - --ignore File with regular expressions for functions and file paths that - should be ignored - -h, --help Print help - -V, --version Print version + Usage: casr-csharp [OPTIONS] <--stdout|--output > [-- ...] + + Arguments: + [ARGS]... Add "-- " to run + + Options: + -o, --output Path to save report. Path can be a directory, then report name + is generated + --stdout Print CASR report to stdout + --stdin Stdin file for program + -t, --timeout Timeout (in seconds) for target execution, 0 value means that + timeout is disabled [default: 0] + --ignore File with regular expressions for functions and file paths that + should be ignored + -h, --help Print help + -V, --version Print version Run casr-csharp: diff --git a/libcasr/src/report.rs b/libcasr/src/report.rs index 52ae6e03..698f60e2 100644 --- a/libcasr/src/report.rs +++ b/libcasr/src/report.rs @@ -227,13 +227,7 @@ pub struct CrashReport { )] #[cfg_attr(feature = "serde", serde(default))] pub js_report: Vec, - /// C# report. - #[cfg_attr( - feature = "serde", serde(rename(serialize = "CSharpReport", deserialize = "CSharpReport")) - )] - #[cfg_attr(feature = "serde", serde(default))] - pub csharp_report: Vec, /// Crash line from stack trace: source:line or binary+offset. #[cfg_attr( feature = "serde", From b11f652c96dd78e3fe7787ccf1adb2528d3f43c2 Mon Sep 17 00:00:00 2001 From: headshog Date: Wed, 6 Mar 2024 22:52:16 +0300 Subject: [PATCH 06/38] casr-afl fix dotnet/mono run --- casr/src/bin/casr-afl.rs | 2 +- casr/src/bin/casr-csharp.rs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/casr/src/bin/casr-afl.rs b/casr/src/bin/casr-afl.rs index bc6c50a1..6adf9760 100644 --- a/casr/src/bin/casr-afl.rs +++ b/casr/src/bin/casr-afl.rs @@ -126,7 +126,7 @@ fn main() -> Result<()> { }; // Get tool. - let tool = if let Some(_) = argv.iter().position(|x| x.ends_with(".dll")) { + let tool = if argv[0].ends_with("dotnet") || argv[0].ends_with("mono") { "casr-csharp" } else { let sym_list = util::symbols_list(Path::new(argv[0]))?; diff --git a/casr/src/bin/casr-csharp.rs b/casr/src/bin/casr-csharp.rs index 59523cee..2ddf2bb1 100644 --- a/casr/src/bin/casr-csharp.rs +++ b/casr/src/bin/casr-csharp.rs @@ -117,7 +117,8 @@ fn main() -> Result<()> { let csharp_stderr_list: Vec = csharp_stderr.split('\n').map(|l| l.to_string()).collect(); //let re = Regex::new(r"(?m)^Unhandled [Ee]xception(?::\n|\. )(?:.|\n)*?((?:[ \n\t]*(?:at [\S ]+|--- End of inner exception stack trace ---))+)$").unwrap(); - let re = Regex::new(r"(?m)^Unhandled [Ee]xception(?::\n|\. )(?:.|\n)*?((?:[ \n\t]*)+)$").unwrap(); + let re = + Regex::new(r"(?m)^Unhandled [Ee]xception(?::\n|\. )(?:.|\n)*?((?:[ \n\t]*)+)$").unwrap(); if let Some(start) = csharp_stderr_list.iter().position(|x| re.is_match(x)) { report.csharp_report = csharp_stderr_list[start..].to_vec(); let report_str = report.csharp_report.join("\n"); From fdf7d4f945dbb90362fb563eac850e6cdf82fc0f Mon Sep 17 00:00:00 2001 From: headshog Date: Thu, 7 Mar 2024 16:26:15 +0300 Subject: [PATCH 07/38] fix regex --- casr/src/bin/casr-csharp.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/casr/src/bin/casr-csharp.rs b/casr/src/bin/casr-csharp.rs index 2ddf2bb1..562c1932 100644 --- a/casr/src/bin/casr-csharp.rs +++ b/casr/src/bin/casr-csharp.rs @@ -102,8 +102,8 @@ fn main() -> Result<()> { // Create report. let mut report = CrashReport::new(); - // Set executable path (for C# .dll file) - if let Some(pos) = argv.iter().position(|x| x.ends_with(".dll")) { + // Set executable path (for C# .dll (dotnet) or .exe (mono) file) + if let Some(pos) = argv.iter().position(|x| {x.ends_with(".dll") || x.ends_with(".exe")}) { let Some(classes) = argv.get(pos) else { bail!("dotnet target is not specified by .dll executable."); }; @@ -116,9 +116,7 @@ fn main() -> Result<()> { // Get C# report. let csharp_stderr_list: Vec = csharp_stderr.split('\n').map(|l| l.to_string()).collect(); - //let re = Regex::new(r"(?m)^Unhandled [Ee]xception(?::\n|\. )(?:.|\n)*?((?:[ \n\t]*(?:at [\S ]+|--- End of inner exception stack trace ---))+)$").unwrap(); - let re = - Regex::new(r"(?m)^Unhandled [Ee]xception(?::\n|\. )(?:.|\n)*?((?:[ \n\t]*)+)$").unwrap(); + let re = Regex::new(r"^Unhandled [Ee]xception(?::\n|\. ).*").unwrap(); if let Some(start) = csharp_stderr_list.iter().position(|x| re.is_match(x)) { report.csharp_report = csharp_stderr_list[start..].to_vec(); let report_str = report.csharp_report.join("\n"); From 325827e6b18c9e9f9cb2aae4efa71192c3737afb Mon Sep 17 00:00:00 2001 From: headshog Date: Thu, 7 Mar 2024 19:47:50 +0300 Subject: [PATCH 08/38] fix style --- casr/src/bin/casr-afl.rs | 33 +++++++++++++++++---------------- casr/src/bin/casr-csharp.rs | 5 ++++- casr/src/bin/casr-libfuzzer.rs | 2 +- 3 files changed, 22 insertions(+), 18 deletions(-) diff --git a/casr/src/bin/casr-afl.rs b/casr/src/bin/casr-afl.rs index 6adf9760..beb8ddf8 100644 --- a/casr/src/bin/casr-afl.rs +++ b/casr/src/bin/casr-afl.rs @@ -129,24 +129,18 @@ fn main() -> Result<()> { let tool = if argv[0].ends_with("dotnet") || argv[0].ends_with("mono") { "casr-csharp" } else { - let sym_list = util::symbols_list(Path::new(argv[0]))?; - if sym_list.contains("__asan") { - "casr-san" - } else { - "casr-gdb" - } + "casr-gdb" }; let tool_path = util::get_path(tool)?; // Get gdb args. let gdb_argv = matches.get_one::("casr-gdb-args"); - let gdb_args = if (tool != "casr-gdb" && tool != "casr-san" && gdb_argv.is_none()) - || matches.get_flag("ignore-cmdline") - { - Vec::new() - } else { - shell_words::split(gdb_argv.unwrap())? - }; + let gdb_args = + if gdb_argv.is_none() || matches.get_flag("ignore-cmdline") { + Vec::new() + } else { + shell_words::split(gdb_argv.unwrap())? + }; // Get all crashes. let mut crashes: HashMap = HashMap::new(); @@ -201,9 +195,16 @@ fn main() -> Result<()> { .map(|x| x + 1); if let Some(target) = crash_info.target_args.first() { - if let Err(e) = util::symbols_list(Path::new(target)) { - error!("{e}"); - continue; + match util::symbols_list(Path::new(target)) { + Ok(list) => { + if list.contains("__asan") { + crash_info.casr_tool = util::get_path("casr-san")?.clone(); + } + } + Err(e) => { + error!("{e}"); + continue; + } } } else { error!("Cmdline is empty. Path: {:?}", path.join("cmdline")); diff --git a/casr/src/bin/casr-csharp.rs b/casr/src/bin/casr-csharp.rs index 562c1932..516497e8 100644 --- a/casr/src/bin/casr-csharp.rs +++ b/casr/src/bin/casr-csharp.rs @@ -103,7 +103,10 @@ fn main() -> Result<()> { // Create report. let mut report = CrashReport::new(); // Set executable path (for C# .dll (dotnet) or .exe (mono) file) - if let Some(pos) = argv.iter().position(|x| {x.ends_with(".dll") || x.ends_with(".exe")}) { + if let Some(pos) = argv + .iter() + .position(|x| x.ends_with(".dll") || x.ends_with(".exe")) + { let Some(classes) = argv.get(pos) else { bail!("dotnet target is not specified by .dll executable."); }; diff --git a/casr/src/bin/casr-libfuzzer.rs b/casr/src/bin/casr-libfuzzer.rs index 607d6736..8d98aff1 100644 --- a/casr/src/bin/casr-libfuzzer.rs +++ b/casr/src/bin/casr-libfuzzer.rs @@ -166,7 +166,7 @@ fn main() -> Result<()> { .collect(); let argv = matches.get_one::("casr-gdb-args"); - let gdb_args = if tool != "casr-gdb" && tool != "casr-san" && argv.is_none() { + let gdb_args = if argv.is_none() { Vec::new() } else { shell_words::split(argv.unwrap())? From 395a0468e2519eee942281e04f71406852ad5111 Mon Sep 17 00:00:00 2001 From: headshog Date: Thu, 7 Mar 2024 21:16:18 +0300 Subject: [PATCH 09/38] fix test afl --- casr/tests/tests.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/casr/tests/tests.rs b/casr/tests/tests.rs index b29d9a20..18c28e73 100644 --- a/casr/tests/tests.rs +++ b/casr/tests/tests.rs @@ -3698,9 +3698,8 @@ fn test_casr_afl() { &paths[0], "-o", &paths[1], - "--", + "--casr-gdb-args", "/tmp/load_sydr", - "@@", ]) .env( "PATH", @@ -3793,7 +3792,7 @@ fn test_casr_afl_ignore_cmd() { &paths[0], "-o", &paths[1], - "--", + "--casr-gdb-args", &load_afl, ]) .output() From 48dcc0cf00387ccba0614552485f9a48827092c0 Mon Sep 17 00:00:00 2001 From: headshog Date: Fri, 8 Mar 2024 00:02:57 +0300 Subject: [PATCH 10/38] casr-afl fix done --- casr/src/bin/casr-afl.rs | 48 +++++++++++++++++++++++++++------------- casr/tests/tests.rs | 2 ++ 2 files changed, 35 insertions(+), 15 deletions(-) diff --git a/casr/src/bin/casr-afl.rs b/casr/src/bin/casr-afl.rs index beb8ddf8..5e98fa6f 100644 --- a/casr/src/bin/casr-afl.rs +++ b/casr/src/bin/casr-afl.rs @@ -7,6 +7,7 @@ use clap::{ Arg, ArgAction, }; use log::error; +use anyhow::bail; use std::collections::HashMap; use std::fs; @@ -112,36 +113,49 @@ fn main() -> Result<()> { // Init log. util::initialize_logging(&matches); + // Get gdb args. + let mut gdb_args = if let Some(argv) = matches.get_one::("casr-gdb-args") { + shell_words::split(argv)? + } else { + Vec::new() + }; + + if gdb_args.is_empty() && matches.get_flag("ignore-cmdline") { + bail!("casr-gdb-args is empty, but \"ignore-cmdline\" option is provided."); + } + // Get fuzz target args (for C#). let mut argv: Vec<&str> = if let Some(argvs) = matches.get_many::("ARGS") { argvs.map(|v| v.as_str()).collect() } else { Vec::new() }; - let at_index = if let Some(idx) = argv.iter().skip(1).position(|s| s.contains("@@")) { - idx + 1 + + let at_index = if gdb_args.is_empty() { + print!("here {:?}\n", argv); + if let Some(idx) = argv.iter().skip(1).position(|s| s.contains("@@")) { + Some(idx + 1) + } else { + argv.push("@@"); + Some(argv.len() - 1) + } } else { - argv.push("@@"); - argv.len() - 1 + if !argv.is_empty() && argv[0] == "@@" { + gdb_args.push("@@".to_string()); + Some(gdb_args.len() - 1) + } else { + None + } }; // Get tool. - let tool = if argv[0].ends_with("dotnet") || argv[0].ends_with("mono") { + let tool = if !argv.is_empty() && (argv[0].ends_with("dotnet") || argv[0].ends_with("mono")) { "casr-csharp" } else { "casr-gdb" }; let tool_path = util::get_path(tool)?; - // Get gdb args. - let gdb_argv = matches.get_one::("casr-gdb-args"); - let gdb_args = - if gdb_argv.is_none() || matches.get_flag("ignore-cmdline") { - Vec::new() - } else { - shell_words::split(gdb_argv.unwrap())? - }; - // Get all crashes. let mut crashes: HashMap = HashMap::new(); for node_dir in fs::read_dir(matches.get_one::("input").unwrap())? { @@ -179,7 +193,7 @@ fn main() -> Result<()> { ] .into_iter() .collect(), - at_index: Some(at_index), + at_index: at_index, ..Default::default() } }; @@ -228,6 +242,10 @@ fn main() -> Result<()> { } } + if matches.get_flag("ignore-cmdline") || tool != "casr-gdb" { + gdb_args = Vec::new(); + } + // Generate reports fuzzing_crash_triage_pipeline(&matches, &crashes, &gdb_args) } diff --git a/casr/tests/tests.rs b/casr/tests/tests.rs index 18c28e73..f838bdba 100644 --- a/casr/tests/tests.rs +++ b/casr/tests/tests.rs @@ -3700,6 +3700,8 @@ fn test_casr_afl() { &paths[1], "--casr-gdb-args", "/tmp/load_sydr", + "--", + "@@", ]) .env( "PATH", From e07e4748ce0defe7a3cb15891d1e09e833caf1cc Mon Sep 17 00:00:00 2001 From: headshog Date: Sun, 10 Mar 2024 13:30:16 +0300 Subject: [PATCH 11/38] tests init --- abc | 318 ++++++++++++++++++ casr/src/bin/casr-afl.rs | 15 +- casr/src/bin/casr-csharp.rs | 4 +- .../casr_tests/csharp/test_casr_csharp.cs | 17 + .../casr_tests/csharp/test_casr_csharp.csproj | 10 + casr/tests/tests.rs | 181 ++++++++++ libcasr/src/execution_class.rs | 2 +- 7 files changed, 535 insertions(+), 12 deletions(-) create mode 100644 abc create mode 100644 casr/tests/casr_tests/csharp/test_casr_csharp.cs create mode 100644 casr/tests/casr_tests/csharp/test_casr_csharp.csproj diff --git a/abc b/abc new file mode 100644 index 00000000..87a13562 --- /dev/null +++ b/abc @@ -0,0 +1,318 @@ +diff --git a/casr/src/bin/casr-afl.rs b/casr/src/bin/casr-afl.rs +index 5e98fa6..81d9269 100644 +--- a/casr/src/bin/casr-afl.rs ++++ b/casr/src/bin/casr-afl.rs +@@ -1,13 +1,13 @@ + use casr::triage::{fuzzing_crash_triage_pipeline, CrashInfo}; + use casr::util; + ++use anyhow::bail; + use anyhow::Result; + use clap::{ + error::{ContextKind, ContextValue, ErrorKind}, + Arg, ArgAction, + }; + use log::error; +-use anyhow::bail; + + use std::collections::HashMap; + use std::fs; +@@ -132,20 +132,17 @@ fn main() -> Result<()> { + }; + + let at_index = if gdb_args.is_empty() { +- print!("here {:?}\n", argv); + if let Some(idx) = argv.iter().skip(1).position(|s| s.contains("@@")) { + Some(idx + 1) + } else { + argv.push("@@"); + Some(argv.len() - 1) + } ++ } else if !argv.is_empty() && argv[0] == "@@" { ++ gdb_args.push("@@".to_string()); ++ Some(gdb_args.len() - 1) + } else { +- if !argv.is_empty() && argv[0] == "@@" { +- gdb_args.push("@@".to_string()); +- Some(gdb_args.len() - 1) +- } else { +- None +- } ++ None + }; + + // Get tool. +@@ -193,7 +190,7 @@ fn main() -> Result<()> { + ] + .into_iter() + .collect(), +- at_index: at_index, ++ at_index, + ..Default::default() + } + }; +diff --git a/casr/src/bin/casr-csharp.rs b/casr/src/bin/casr-csharp.rs +index 516497e..d467929 100644 +--- a/casr/src/bin/casr-csharp.rs ++++ b/casr/src/bin/casr-csharp.rs +@@ -105,10 +105,10 @@ fn main() -> Result<()> { + // Set executable path (for C# .dll (dotnet) or .exe (mono) file) + if let Some(pos) = argv + .iter() +- .position(|x| x.ends_with(".dll") || x.ends_with(".exe")) ++ .position(|x| x.ends_with(".dll") || x.ends_with(".exe") || x.ends_with(".csproj")) + { + let Some(classes) = argv.get(pos) else { +- bail!("dotnet target is not specified by .dll executable."); ++ bail!("dotnet target is not specified by .dll, .exe or .csproj executable."); + }; + report.executable_path = classes.to_string(); + } +diff --git a/casr/tests/casr_tests/csharp/test_casr_csharp.cs b/casr/tests/casr_tests/csharp/test_casr_csharp.cs +new file mode 100644 +index 0000000..9ff4fa3 +--- /dev/null ++++ b/casr/tests/casr_tests/csharp/test_casr_csharp.cs +@@ -0,0 +1,17 @@ ++using System; ++ ++public class Program ++{ ++ public static void Main(string[] args) ++ { ++ f1(); ++ } ++ ++ public static void f1() { ++ f2(); ++ } ++ ++ public static void f2() { ++ throw new ArgumentException("Parameter cannot be null"); ++ } ++} +diff --git a/casr/tests/casr_tests/csharp/test_casr_csharp.csproj b/casr/tests/casr_tests/csharp/test_casr_csharp.csproj +new file mode 100644 +index 0000000..206b89a +--- /dev/null ++++ b/casr/tests/casr_tests/csharp/test_casr_csharp.csproj +@@ -0,0 +1,10 @@ ++ ++ ++ ++ Exe ++ net8.0 ++ enable ++ enable ++ ++ ++ +diff --git a/casr/tests/tests.rs b/casr/tests/tests.rs +index 0a16fe5..c4c4047 100644 +--- a/casr/tests/tests.rs ++++ b/casr/tests/tests.rs +@@ -22,6 +22,7 @@ lazy_static::lazy_static! { + static ref EXE_CASR_PYTHON: RwLock<&'static str> = RwLock::new(env!("CARGO_BIN_EXE_casr-python")); + static ref EXE_CASR_JAVA: RwLock<&'static str> = RwLock::new(env!("CARGO_BIN_EXE_casr-java")); + static ref EXE_CASR_JS: RwLock<&'static str> = RwLock::new(env!("CARGO_BIN_EXE_casr-js")); ++ static ref EXE_CASR_CSHARP: RwLock<&'static str> = RwLock::new(env!("CARGO_BIN_EXE_casr-csharp")); + static ref EXE_CASR_GDB: RwLock<&'static str> = RwLock::new(env!("CARGO_BIN_EXE_casr-gdb")); + static ref PROJECT_DIR: RwLock<&'static str> = RwLock::new(env!("CARGO_MANIFEST_DIR")); + } +@@ -5705,3 +5706,183 @@ fn test_casr_libfuzzer_jazzer_js_xml2js() { + + assert!(storage.values().all(|x| *x > 1)); + } ++ ++#[test] ++#[cfg(target_arch = "x86_64")] ++fn test_casr_csharp() { ++ let paths = [ ++ abs_path("tests/casr_tests/csharp/test_casr_csharp.cs"), ++ abs_path("tests/casr_tests/csharp/test_casr_csharp.csproj"), ++ abs_path("tests/tmp_tests_casr/test_casr_csharp"), ++ abs_path("tests/tmp_tests_casr/test_casr_csharp/test_casr_csharp.cs"), ++ abs_path("tests/tmp_tests_casr/test_casr_csharp/test_casr_csharp.csproj"), ++ ]; ++ let _ = std::fs::create_dir_all(&paths[2]); ++ let _ = fs::copy(&paths[0], &paths[3]); ++ let _ = fs::copy(&paths[1], &paths[4]); ++ let Ok(dotnet_path) = which::which("dotnet") else { ++ panic!("No dotnet is found."); ++ }; ++ ++ let output = Command::new(*EXE_CASR_CSHARP.read().unwrap()) ++ .args([ ++ "--stdout", ++ "--", ++ &dotnet_path.to_str().unwrap(), ++ "run", ++ "--project", ++ &paths[4], ++ ]) ++ .output() ++ .expect("failed to start casr-csharp"); ++ ++ assert!( ++ output.status.success(), ++ "Stdout {}.\n Stderr: {}", ++ String::from_utf8_lossy(&output.stdout), ++ String::from_utf8_lossy(&output.stderr) ++ ); ++ ++ let report: Result = serde_json::from_slice(&output.stdout); ++ if let Ok(report) = report { ++ let severity_type = report["CrashSeverity"]["Type"].as_str().unwrap(); ++ let severity_desc = report["CrashSeverity"]["ShortDescription"] ++ .as_str() ++ .unwrap() ++ .to_string(); ++ ++ assert_eq!(3, report["Stacktrace"].as_array().unwrap().iter().count()); ++ assert_eq!(severity_type, "UNDEFINED"); ++ assert_eq!(severity_desc, "System.ArgumentException"); ++ assert!(report["CrashLine"] ++ .as_str() ++ .unwrap() ++ .contains("test_casr_csharp.cs:15")); ++ } else { ++ panic!("Couldn't parse json report file."); ++ } ++} ++ ++#[test] ++#[cfg(target_arch = "x86_64")] ++fn test_casr_afl_csharp() { ++ /*use std::collections::HashMap; ++ ++ let test_dir = abs_path("tests/tmp_tests_casr/test_casr_libfuzzer_jazzer_js"); ++ let _ = std::fs::remove_dir_all(test_dir); ++ let paths = [ ++ abs_path("tests/casr_tests/js/test_casr_libfuzzer_jazzer_js.js"), ++ abs_path("tests/tmp_tests_casr/test_casr_libfuzzer_jazzer_js/crashes"), ++ abs_path("tests/tmp_tests_casr/test_casr_libfuzzer_jazzer_js/casr_out"), ++ abs_path( ++ "tests/tmp_tests_casr/test_casr_libfuzzer_jazzer_js/test_casr_libfuzzer_jazzer_js.js", ++ ), ++ ]; ++ ++ // Create crashes dir ++ let output = Command::new("mkdir") ++ .args(["-p", &paths[1]]) ++ .output() ++ .expect("failed to create dir"); ++ assert!( ++ output.status.success(), ++ "Stdout {}.\n Stderr: {}", ++ String::from_utf8_lossy(&output.stdout), ++ String::from_utf8_lossy(&output.stderr) ++ ); ++ ++ let _ = fs::copy(&paths[0], &paths[3]); ++ ++ let Ok(npx_path) = which::which("npx") else { ++ panic!("No npx is found."); ++ }; ++ ++ Command::new("unzip") ++ .arg(abs_path("tests/casr_tests/js/crashes.zip")) ++ .args(["-d", &paths[1]]) ++ .stdout(Stdio::null()) ++ .status() ++ .expect("failed to unzip crashes.zip"); ++ ++ 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[1], ++ "-o", ++ &paths[2], ++ "--", ++ npx_path.to_str().unwrap(), ++ "jazzer", ++ &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("NOT_EXPLOITABLE")); ++ assert!(err.contains("TypeError")); ++ assert!(err.contains("ReferenceError")); ++ assert!(err.contains("RangeError")); ++ assert!(err.contains("test_casr_libfuzzer_jazzer_js.js")); ++ ++ let re = Regex::new(r"Number of reports after deduplication: (?P\d+)").unwrap(); ++ let unique_cnt = re ++ .captures(&err) ++ .unwrap() ++ .name("unique") ++ .map(|x| x.as_str()) ++ .unwrap() ++ .parse::() ++ .unwrap(); ++ ++ assert_eq!(unique_cnt, 3, "Invalid number of deduplicated reports"); ++ ++ let re = Regex::new(r"Number of clusters: (?P\d+)").unwrap(); ++ let clusters_cnt = re ++ .captures(&err) ++ .unwrap() ++ .name("clusters") ++ .map(|x| x.as_str()) ++ .unwrap() ++ .parse::() ++ .unwrap(); ++ ++ assert_eq!(clusters_cnt, 1, "Invalid number of clusters"); ++ ++ let mut storage: HashMap = HashMap::new(); ++ for entry in fs::read_dir(&paths[2]).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));*/ ++} +diff --git a/libcasr/src/execution_class.rs b/libcasr/src/execution_class.rs +index ab69db5..91f6e76 100644 +--- a/libcasr/src/execution_class.rs ++++ b/libcasr/src/execution_class.rs +@@ -204,7 +204,7 @@ impl Default for ExecutionClass { + severity: "UNDEFINED".to_string(), + short_description: "Undefined".to_string(), + description: "Undefined class".to_string(), +- explanation: "The is no execution class for this type of exception".to_string(), ++ explanation: "There is no execution class for this type of exception".to_string(), + } + } + } diff --git a/casr/src/bin/casr-afl.rs b/casr/src/bin/casr-afl.rs index 5e98fa6f..81d9269c 100644 --- a/casr/src/bin/casr-afl.rs +++ b/casr/src/bin/casr-afl.rs @@ -1,13 +1,13 @@ use casr::triage::{fuzzing_crash_triage_pipeline, CrashInfo}; use casr::util; +use anyhow::bail; use anyhow::Result; use clap::{ error::{ContextKind, ContextValue, ErrorKind}, Arg, ArgAction, }; use log::error; -use anyhow::bail; use std::collections::HashMap; use std::fs; @@ -132,20 +132,17 @@ fn main() -> Result<()> { }; let at_index = if gdb_args.is_empty() { - print!("here {:?}\n", argv); if let Some(idx) = argv.iter().skip(1).position(|s| s.contains("@@")) { Some(idx + 1) } else { argv.push("@@"); Some(argv.len() - 1) } + } else if !argv.is_empty() && argv[0] == "@@" { + gdb_args.push("@@".to_string()); + Some(gdb_args.len() - 1) } else { - if !argv.is_empty() && argv[0] == "@@" { - gdb_args.push("@@".to_string()); - Some(gdb_args.len() - 1) - } else { - None - } + None }; // Get tool. @@ -193,7 +190,7 @@ fn main() -> Result<()> { ] .into_iter() .collect(), - at_index: at_index, + at_index, ..Default::default() } }; diff --git a/casr/src/bin/casr-csharp.rs b/casr/src/bin/casr-csharp.rs index 516497e8..d4679296 100644 --- a/casr/src/bin/casr-csharp.rs +++ b/casr/src/bin/casr-csharp.rs @@ -105,10 +105,10 @@ fn main() -> Result<()> { // Set executable path (for C# .dll (dotnet) or .exe (mono) file) if let Some(pos) = argv .iter() - .position(|x| x.ends_with(".dll") || x.ends_with(".exe")) + .position(|x| x.ends_with(".dll") || x.ends_with(".exe") || x.ends_with(".csproj")) { let Some(classes) = argv.get(pos) else { - bail!("dotnet target is not specified by .dll executable."); + bail!("dotnet target is not specified by .dll, .exe or .csproj executable."); }; report.executable_path = classes.to_string(); } diff --git a/casr/tests/casr_tests/csharp/test_casr_csharp.cs b/casr/tests/casr_tests/csharp/test_casr_csharp.cs new file mode 100644 index 00000000..9ff4fa3e --- /dev/null +++ b/casr/tests/casr_tests/csharp/test_casr_csharp.cs @@ -0,0 +1,17 @@ +using System; + +public class Program +{ + public static void Main(string[] args) + { + f1(); + } + + public static void f1() { + f2(); + } + + public static void f2() { + throw new ArgumentException("Parameter cannot be null"); + } +} diff --git a/casr/tests/casr_tests/csharp/test_casr_csharp.csproj b/casr/tests/casr_tests/csharp/test_casr_csharp.csproj new file mode 100644 index 00000000..206b89a9 --- /dev/null +++ b/casr/tests/casr_tests/csharp/test_casr_csharp.csproj @@ -0,0 +1,10 @@ + + + + Exe + net8.0 + enable + enable + + + diff --git a/casr/tests/tests.rs b/casr/tests/tests.rs index f838bdba..7dfc8611 100644 --- a/casr/tests/tests.rs +++ b/casr/tests/tests.rs @@ -22,6 +22,7 @@ lazy_static::lazy_static! { static ref EXE_CASR_PYTHON: RwLock<&'static str> = RwLock::new(env!("CARGO_BIN_EXE_casr-python")); static ref EXE_CASR_JAVA: RwLock<&'static str> = RwLock::new(env!("CARGO_BIN_EXE_casr-java")); static ref EXE_CASR_JS: RwLock<&'static str> = RwLock::new(env!("CARGO_BIN_EXE_casr-js")); + static ref EXE_CASR_CSHARP: RwLock<&'static str> = RwLock::new(env!("CARGO_BIN_EXE_casr-csharp")); static ref EXE_CASR_GDB: RwLock<&'static str> = RwLock::new(env!("CARGO_BIN_EXE_casr-gdb")); static ref PROJECT_DIR: RwLock<&'static str> = RwLock::new(env!("CARGO_MANIFEST_DIR")); } @@ -5736,3 +5737,183 @@ fn test_casr_libfuzzer_jazzer_js_xml2js() { assert!(storage.values().all(|x| *x > 1)); } + +#[test] +#[cfg(target_arch = "x86_64")] +fn test_casr_csharp() { + let paths = [ + abs_path("tests/casr_tests/csharp/test_casr_csharp.cs"), + abs_path("tests/casr_tests/csharp/test_casr_csharp.csproj"), + abs_path("tests/tmp_tests_casr/test_casr_csharp"), + abs_path("tests/tmp_tests_casr/test_casr_csharp/test_casr_csharp.cs"), + abs_path("tests/tmp_tests_casr/test_casr_csharp/test_casr_csharp.csproj"), + ]; + let _ = std::fs::create_dir_all(&paths[2]); + let _ = fs::copy(&paths[0], &paths[3]); + let _ = fs::copy(&paths[1], &paths[4]); + let Ok(dotnet_path) = which::which("dotnet") else { + panic!("No dotnet is found."); + }; + + let output = Command::new(*EXE_CASR_CSHARP.read().unwrap()) + .args([ + "--stdout", + "--", + &dotnet_path.to_str().unwrap(), + "run", + "--project", + &paths[4], + ]) + .output() + .expect("failed to start casr-csharp"); + + assert!( + output.status.success(), + "Stdout {}.\n Stderr: {}", + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + ); + + let report: Result = serde_json::from_slice(&output.stdout); + if let Ok(report) = report { + let severity_type = report["CrashSeverity"]["Type"].as_str().unwrap(); + let severity_desc = report["CrashSeverity"]["ShortDescription"] + .as_str() + .unwrap() + .to_string(); + + assert_eq!(3, report["Stacktrace"].as_array().unwrap().iter().count()); + assert_eq!(severity_type, "UNDEFINED"); + assert_eq!(severity_desc, "System.ArgumentException"); + assert!(report["CrashLine"] + .as_str() + .unwrap() + .contains("test_casr_csharp.cs:15")); + } else { + panic!("Couldn't parse json report file."); + } +} + +#[test] +#[cfg(target_arch = "x86_64")] +fn test_casr_afl_csharp() { + /*use std::collections::HashMap; + + let test_dir = abs_path("tests/tmp_tests_casr/test_casr_libfuzzer_jazzer_js"); + let _ = std::fs::remove_dir_all(test_dir); + let paths = [ + abs_path("tests/casr_tests/js/test_casr_libfuzzer_jazzer_js.js"), + abs_path("tests/tmp_tests_casr/test_casr_libfuzzer_jazzer_js/crashes"), + abs_path("tests/tmp_tests_casr/test_casr_libfuzzer_jazzer_js/casr_out"), + abs_path( + "tests/tmp_tests_casr/test_casr_libfuzzer_jazzer_js/test_casr_libfuzzer_jazzer_js.js", + ), + ]; + + // Create crashes dir + let output = Command::new("mkdir") + .args(["-p", &paths[1]]) + .output() + .expect("failed to create dir"); + assert!( + output.status.success(), + "Stdout {}.\n Stderr: {}", + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + ); + + let _ = fs::copy(&paths[0], &paths[3]); + + let Ok(npx_path) = which::which("npx") else { + panic!("No npx is found."); + }; + + Command::new("unzip") + .arg(abs_path("tests/casr_tests/js/crashes.zip")) + .args(["-d", &paths[1]]) + .stdout(Stdio::null()) + .status() + .expect("failed to unzip crashes.zip"); + + 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[1], + "-o", + &paths[2], + "--", + npx_path.to_str().unwrap(), + "jazzer", + &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("NOT_EXPLOITABLE")); + assert!(err.contains("TypeError")); + assert!(err.contains("ReferenceError")); + assert!(err.contains("RangeError")); + assert!(err.contains("test_casr_libfuzzer_jazzer_js.js")); + + let re = Regex::new(r"Number of reports after deduplication: (?P\d+)").unwrap(); + let unique_cnt = re + .captures(&err) + .unwrap() + .name("unique") + .map(|x| x.as_str()) + .unwrap() + .parse::() + .unwrap(); + + assert_eq!(unique_cnt, 3, "Invalid number of deduplicated reports"); + + let re = Regex::new(r"Number of clusters: (?P\d+)").unwrap(); + let clusters_cnt = re + .captures(&err) + .unwrap() + .name("clusters") + .map(|x| x.as_str()) + .unwrap() + .parse::() + .unwrap(); + + assert_eq!(clusters_cnt, 1, "Invalid number of clusters"); + + let mut storage: HashMap = HashMap::new(); + for entry in fs::read_dir(&paths[2]).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));*/ +} diff --git a/libcasr/src/execution_class.rs b/libcasr/src/execution_class.rs index ab69db5e..91f6e76a 100644 --- a/libcasr/src/execution_class.rs +++ b/libcasr/src/execution_class.rs @@ -204,7 +204,7 @@ impl Default for ExecutionClass { severity: "UNDEFINED".to_string(), short_description: "Undefined".to_string(), description: "Undefined class".to_string(), - explanation: "The is no execution class for this type of exception".to_string(), + explanation: "There is no execution class for this type of exception".to_string(), } } } From 3852f5cdbffc37fc94a1f3c22380f1cb0ede8e9d Mon Sep 17 00:00:00 2001 From: headshog Date: Tue, 12 Mar 2024 18:51:59 +0300 Subject: [PATCH 12/38] tests and casr-afl done --- .github/workflows/aarch64.yml | 3 - .github/workflows/amd64.yml | 5 +- .github/workflows/coverage.yaml | 5 +- README.md | 8 +- casr/src/bin/casr-afl.rs | 54 +++++---- casr/src/bin/casr-libfuzzer.rs | 34 ++++-- .../afl-out-sharpfuzz/afl_main-worker/cmdline | 3 + ...2,src:000001,time:0,execs:9,op:havoc,rep:1 | 1 + ...,src:000001,time:0,execs:12,op:havoc,rep:3 | Bin 0 -> 11 bytes ...,src:000001,time:3,execs:27,op:havoc,rep:3 | 1 + .../afl_main-worker/fuzzer_stats | 43 +++++++ .../afl-out-sharpfuzz/afl_s01-worker/cmdline | 3 + ...,src:000001,time:0,execs:10,op:havoc,rep:1 | 1 + ...,src:000001,time:1,execs:12,op:havoc,rep:1 | Bin 0 -> 4 bytes ...,src:000001,time:2,execs:24,op:havoc,rep:8 | 1 + .../afl_s01-worker/fuzzer_stats | 43 +++++++ .../test_casr_afl_csharp.cs | 24 ++++ .../test_casr_afl_csharp.csproj | 15 +++ .../test_casr_afl_csharp_module.cs | 21 ++++ .../test_casr_afl_csharp_module.csproj | 9 ++ casr/tests/tests.rs | 111 ++++++++---------- docs/usage.md | 14 +-- 22 files changed, 279 insertions(+), 120 deletions(-) create mode 100644 casr/tests/casr_tests/casrep/afl-out-sharpfuzz/afl_main-worker/cmdline create mode 100644 casr/tests/casr_tests/casrep/afl-out-sharpfuzz/afl_main-worker/crashes/id:000000,sig:02,src:000001,time:0,execs:9,op:havoc,rep:1 create mode 100644 casr/tests/casr_tests/casrep/afl-out-sharpfuzz/afl_main-worker/crashes/id:000001,sig:02,src:000001,time:0,execs:12,op:havoc,rep:3 create mode 100644 casr/tests/casr_tests/casrep/afl-out-sharpfuzz/afl_main-worker/crashes/id:000002,sig:02,src:000001,time:3,execs:27,op:havoc,rep:3 create mode 100644 casr/tests/casr_tests/casrep/afl-out-sharpfuzz/afl_main-worker/fuzzer_stats create mode 100644 casr/tests/casr_tests/casrep/afl-out-sharpfuzz/afl_s01-worker/cmdline create mode 100644 casr/tests/casr_tests/casrep/afl-out-sharpfuzz/afl_s01-worker/crashes/id:000000,sig:02,src:000001,time:0,execs:10,op:havoc,rep:1 create mode 100644 casr/tests/casr_tests/casrep/afl-out-sharpfuzz/afl_s01-worker/crashes/id:000001,sig:02,src:000001,time:1,execs:12,op:havoc,rep:1 create mode 100644 casr/tests/casr_tests/casrep/afl-out-sharpfuzz/afl_s01-worker/crashes/id:000002,sig:02,src:000001,time:2,execs:24,op:havoc,rep:8 create mode 100644 casr/tests/casr_tests/casrep/afl-out-sharpfuzz/afl_s01-worker/fuzzer_stats create mode 100644 casr/tests/casr_tests/csharp/test_casr_afl_csharp/test_casr_afl_csharp.cs create mode 100644 casr/tests/casr_tests/csharp/test_casr_afl_csharp/test_casr_afl_csharp.csproj create mode 100644 casr/tests/casr_tests/csharp/test_casr_afl_csharp_module/test_casr_afl_csharp_module.cs create mode 100644 casr/tests/casr_tests/csharp/test_casr_afl_csharp_module/test_casr_afl_csharp_module.csproj diff --git a/.github/workflows/aarch64.yml b/.github/workflows/aarch64.yml index 7616c910..92f8f8b1 100644 --- a/.github/workflows/aarch64.yml +++ b/.github/workflows/aarch64.yml @@ -32,9 +32,6 @@ jobs: export PATH=$PATH:/dotnet export DOTNET_CLI_TELEMETRY_OPTOUT=1 export DOTNET_UPGRADEASSISTANT_TELEMETRY_OPTOUT=1 - dotnet tool install SharpFuzz.CommandLine --global - dotnet tool install minicover --global - export PATH=$PATH:/root/.dotnet/tools curl https://sh.rustup.rs -o rustup.sh && chmod +x rustup.sh && \ ./rustup.sh -y && rm rustup.sh run: | diff --git a/.github/workflows/amd64.yml b/.github/workflows/amd64.yml index c696ef63..bd264b87 100644 --- a/.github/workflows/amd64.yml +++ b/.github/workflows/amd64.yml @@ -31,10 +31,7 @@ jobs: sudo npm install --save-dev @jazzer.js/core wget -q https://packages.microsoft.com/config/ubuntu/22.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb dpkg -i packages-microsoft-prod.deb && rm packages-microsoft-prod.deb - apt update && apt install -y --no-install-recommends dotnet-sdk-8.0 - dotnet tool install SharpFuzz.CommandLine --global - dotnet tool install minicover --global - export PATH=$PATH:/root/.dotnet/tools + sudo apt update && sudo apt install -y --no-install-recommends dotnet-sdk-8.0 curl https://sh.rustup.rs -o rustup.sh && chmod +x rustup.sh && \ ./rustup.sh -y && rm rustup.sh rustup install nightly diff --git a/.github/workflows/coverage.yaml b/.github/workflows/coverage.yaml index ee75b225..12f23c35 100644 --- a/.github/workflows/coverage.yaml +++ b/.github/workflows/coverage.yaml @@ -29,10 +29,7 @@ jobs: sudo npm install --save-dev @jazzer.js/core wget -q https://packages.microsoft.com/config/ubuntu/22.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb dpkg -i packages-microsoft-prod.deb && rm packages-microsoft-prod.deb - apt update && apt install -y --no-install-recommends dotnet-sdk-8.0 - dotnet tool install SharpFuzz.CommandLine --global - dotnet tool install minicover --global - export PATH=$PATH:/root/.dotnet/tools + sudo apt update && sudo apt install -y --no-install-recommends dotnet-sdk-8.0 curl https://sh.rustup.rs -o rustup.sh && chmod +x rustup.sh && \ ./rustup.sh -y && rm rustup.sh rustup install nightly diff --git a/README.md b/README.md index 82b272d3..810d1645 100644 --- a/README.md +++ b/README.md @@ -167,7 +167,7 @@ Create report from JavaScript: Create report from C#: - $ casr-csharp -o csharp.casrep -- dotnet casr/tests/casr_tests/csharp/test_casr_csharp.dll + $ casr-csharp -o csharp.casrep -- dotnet run --project casr/tests/casr_tests/csharp/bin/test_casr_csharp.csproj View report: @@ -201,6 +201,12 @@ Triage crashes after AFL++ fuzzing with casr-afl: $ # You may also additionally generate crash reports for uninstrumented binary with casr-gdb $ casr-afl -i casr/tests/casr_tests/casrep/afl-out-xlnt -o casr/tests/tmp_tests_casr/casr_afl_out -- /tmp/load_sydr @@ +Triage crashes after Sharpfuzz fuzzing with casr-afl: + + $ cp casr/tests/casr_tests/csharp/test_casr_afl_csharp /tmp/test_casr_afl_csharp + $ cp casr/tests/casr_tests/csharp/test_casr_afl_csharp_module /tmp/test_casr_afl_csharp_module + $ casr-afl -i casr/tests/casr_tests/casrep/afl-out-sharpfuzz -o casr/tests/tmp_tests_casr/casr_afl_csharp_out -- dotnet run --project /tmp/test_casr_afl_csharp/test_casr_afl_csharp.csproj @@ + Triage libFuzzer crashes with casr-libfuzzer: $ casr-libfuzzer -t 30 -i casr/tests/casr_tests/casrep/libfuzzer_crashes_xlnt -o casr/tests/tmp_tests_casr/casr_libfuzzer_out -- casr/tests/casr_tests/bin/load_fuzzer diff --git a/casr/src/bin/casr-afl.rs b/casr/src/bin/casr-afl.rs index 81d9269c..7b4d4bab 100644 --- a/casr/src/bin/casr-afl.rs +++ b/casr/src/bin/casr-afl.rs @@ -99,6 +99,7 @@ fn main() -> Result<()> { Arg::new("casr-gdb-args") .long("casr-gdb-args") .action(ArgAction::Set) + .num_args(1..) .help("Add \"--casr-gdb-args \'./gdb_fuzz_target \'\" to generate additional crash reports with casr-gdb (e.g., test whether program crashes without sanitizers)"), ) .arg( @@ -113,37 +114,22 @@ fn main() -> Result<()> { // Init log. util::initialize_logging(&matches); - // Get gdb args. - let mut gdb_args = if let Some(argv) = matches.get_one::("casr-gdb-args") { - shell_words::split(argv)? - } else { - Vec::new() - }; - - if gdb_args.is_empty() && matches.get_flag("ignore-cmdline") { - bail!("casr-gdb-args is empty, but \"ignore-cmdline\" option is provided."); - } - - // Get fuzz target args (for C#). - let mut argv: Vec<&str> = if let Some(argvs) = matches.get_many::("ARGS") { + // Get fuzz target args. + let argv: Vec<&str> = if let Some(argvs) = matches.get_many::("ARGS") { argvs.map(|v| v.as_str()).collect() } else { Vec::new() }; - let at_index = if gdb_args.is_empty() { - if let Some(idx) = argv.iter().skip(1).position(|s| s.contains("@@")) { - Some(idx + 1) - } else { - argv.push("@@"); - Some(argv.len() - 1) - } - } else if !argv.is_empty() && argv[0] == "@@" { - gdb_args.push("@@".to_string()); - Some(gdb_args.len() - 1) + // Get gdb args. + let mut gdb_args = if let Some(argv) = matches.get_many::("casr-gdb-args") { + argv.cloned().collect() } else { - None + Vec::new() }; + if gdb_args.is_empty() && matches.get_flag("ignore-cmdline") { + bail!("casr-gdb-args is empty, but \"ignore-cmdline\" option is provided."); + } // Get tool. let tool = if !argv.is_empty() && (argv[0].ends_with("dotnet") || argv[0].ends_with("mono")) { @@ -153,6 +139,24 @@ fn main() -> Result<()> { }; let tool_path = util::get_path(tool)?; + if !gdb_args.is_empty() && tool != "casr-gdb" { + bail!("casr-gdb-args is provided with other tool (casr-csharp)."); + } + + // Get input file argument index. + let at_index = if gdb_args.is_empty() { + argv.iter() + .skip(1) + .position(|s| s.contains("@@")) + .map(|x| x + 1) + } else { + gdb_args + .iter() + .skip(1) + .position(|s| s.contains("@@")) + .map(|x| x + 1) + }; + // Get all crashes. let mut crashes: HashMap = HashMap::new(); for node_dir in fs::read_dir(matches.get_one::("input").unwrap())? { @@ -239,7 +243,7 @@ fn main() -> Result<()> { } } - if matches.get_flag("ignore-cmdline") || tool != "casr-gdb" { + if matches.get_flag("ignore-cmdline") { gdb_args = Vec::new(); } diff --git a/casr/src/bin/casr-libfuzzer.rs b/casr/src/bin/casr-libfuzzer.rs index 8d98aff1..b2d7f8f2 100644 --- a/casr/src/bin/casr-libfuzzer.rs +++ b/casr/src/bin/casr-libfuzzer.rs @@ -91,6 +91,7 @@ fn main() -> Result<()> { Arg::new("casr-gdb-args") .long("casr-gdb-args") .action(ArgAction::Set) + .num_args(1..) .help("Add \"--casr-gdb-args \'./gdb_fuzz_target \'\" to generate additional crash reports with casr-gdb (e.g., test whether program crashes without sanitizers)"), ) .arg( @@ -98,6 +99,7 @@ fn main() -> Result<()> { .action(ArgAction::Set) .num_args(1..) .last(true) + .required(true) .help("Add \"-- ./fuzz_target \""), ) .get_matches(); @@ -112,17 +114,18 @@ fn main() -> Result<()> { let mut argv: Vec<&str> = if let Some(argvs) = matches.get_many::("ARGS") { argvs.map(|v| v.as_str()).collect() } else { - bail!("Invalid fuzz target arguments"); + Vec::new() }; - let at_index = if let Some(idx) = argv.iter().skip(1).position(|s| s.contains("@@")) { - idx + 1 + + // Get gdb args. + let gdb_args = if let Some(argv) = matches.get_many::("casr-gdb-args") { + argv.cloned().collect() } else { - argv.push("@@"); - argv.len() - 1 + Vec::new() }; + // Get tool. let mut envs = HashMap::new(); - let tool = if argv[0].ends_with(".py") { envs.insert("LD_PRELOAD".to_string(), util::get_atheris_lib()?); "casr-python" @@ -144,6 +147,18 @@ fn main() -> Result<()> { }; let tool_path = util::get_path(tool)?; + if !gdb_args.is_empty() && tool != "casr-gdb" { + bail!("casr-gdb-args is provided with other tool (casr-python or casr-java or casr-js)."); + } + + // Get input file argument index. + let at_index = if let Some(idx) = argv.iter().skip(1).position(|s| s.contains("@@")) { + idx + 1 + } else { + argv.push("@@"); + argv.len() - 1 + }; + // Get all crashes. let crashes: HashMap = fs::read_dir(input_dir)? .flatten() @@ -165,13 +180,6 @@ fn main() -> Result<()> { }) .collect(); - let argv = matches.get_one::("casr-gdb-args"); - let gdb_args = if argv.is_none() { - Vec::new() - } else { - shell_words::split(argv.unwrap())? - }; - // Generate reports fuzzing_crash_triage_pipeline(&matches, &crashes, &gdb_args) } diff --git a/casr/tests/casr_tests/casrep/afl-out-sharpfuzz/afl_main-worker/cmdline b/casr/tests/casr_tests/casrep/afl-out-sharpfuzz/afl_main-worker/cmdline new file mode 100644 index 00000000..e5ec54e9 --- /dev/null +++ b/casr/tests/casr_tests/casrep/afl-out-sharpfuzz/afl_main-worker/cmdline @@ -0,0 +1,3 @@ +/usr/bin/dotnet +/build_test/test_casr_afl_csharp/bin/test_casr_afl_csharp.dll +@@ diff --git a/casr/tests/casr_tests/casrep/afl-out-sharpfuzz/afl_main-worker/crashes/id:000000,sig:02,src:000001,time:0,execs:9,op:havoc,rep:1 b/casr/tests/casr_tests/casrep/afl-out-sharpfuzz/afl_main-worker/crashes/id:000000,sig:02,src:000001,time:0,execs:9,op:havoc,rep:1 new file mode 100644 index 00000000..ea69c037 --- /dev/null +++ b/casr/tests/casr_tests/casrep/afl-out-sharpfuzz/afl_main-worker/crashes/id:000000,sig:02,src:000001,time:0,execs:9,op:havoc,rep:1 @@ -0,0 +1 @@ +dadxxxx diff --git a/casr/tests/casr_tests/casrep/afl-out-sharpfuzz/afl_main-worker/crashes/id:000001,sig:02,src:000001,time:0,execs:12,op:havoc,rep:3 b/casr/tests/casr_tests/casrep/afl-out-sharpfuzz/afl_main-worker/crashes/id:000001,sig:02,src:000001,time:0,execs:12,op:havoc,rep:3 new file mode 100644 index 0000000000000000000000000000000000000000..f10969ae0e7e24bd04e96af19c3e3468f0e1f1ea GIT binary patch literal 11 ScmYeA_aeWjfSG}zf(rl~)C0c& literal 0 HcmV?d00001 diff --git a/casr/tests/casr_tests/casrep/afl-out-sharpfuzz/afl_main-worker/crashes/id:000002,sig:02,src:000001,time:3,execs:27,op:havoc,rep:3 b/casr/tests/casr_tests/casrep/afl-out-sharpfuzz/afl_main-worker/crashes/id:000002,sig:02,src:000001,time:3,execs:27,op:havoc,rep:3 new file mode 100644 index 00000000..c972f284 --- /dev/null +++ b/casr/tests/casr_tests/casrep/afl-out-sharpfuzz/afl_main-worker/crashes/id:000002,sig:02,src:000001,time:3,execs:27,op:havoc,rep:3 @@ -0,0 +1 @@ +baaaaa€Exxxx diff --git a/casr/tests/casr_tests/casrep/afl-out-sharpfuzz/afl_main-worker/fuzzer_stats b/casr/tests/casr_tests/casrep/afl-out-sharpfuzz/afl_main-worker/fuzzer_stats new file mode 100644 index 00000000..c25403fb --- /dev/null +++ b/casr/tests/casr_tests/casrep/afl-out-sharpfuzz/afl_main-worker/fuzzer_stats @@ -0,0 +1,43 @@ +start_time : 1710252309 +last_update : 1710252326 +run_time : 17 +fuzzer_pid : 1465 +cycles_done : 143 +cycles_wo_finds : 143 +time_wo_finds : 0 +execs_done : 187893 +execs_per_sec : 10919.57 +execs_ps_last_min : 0.00 +corpus_count : 2 +corpus_favored : 1 +corpus_found : 0 +corpus_imported : 0 +corpus_variable : 0 +max_depth : 1 +cur_item : 2 +pending_favs : 0 +pending_total : 0 +stability : 100.00% +bitmap_cvg : 0.01% +saved_crashes : 3 +saved_hangs : 0 +last_find : 0 +last_crash : 1710252309 +last_hang : 0 +execs_since_crash : 187866 +exec_timeout : 10000 +slowest_exec_ms : 0 +peak_rss_mb : 35 +cpu_affinity : 0 +edges_found : 6 +total_edges : 65536 +var_byte_count : 0 +havoc_expansion : 5 +auto_dict_entries : 0 +testcache_size : 21 +testcache_count : 2 +testcache_evict : 0 +afl_banner : /build_test/test_casr_afl_csharp/bin/test_casr_afl_csharp.dll#afl_main +afl_version : ++4.09c +target_mode : shmem_testcase default +command_line : /usr/local/bin/afl-fuzz -T /build_test/test_casr_afl_csharp/bin/test_casr_afl_csharp.dll#afl_main -M afl_main-worker -i /fuzz/test-out/corpus -t 10000 -o /fuzz/test-out/aflplusplus -- /usr/bin/dotnet /build_test/test_casr_afl_csharp/bin/test_casr_afl_csharp.dll @@ diff --git a/casr/tests/casr_tests/casrep/afl-out-sharpfuzz/afl_s01-worker/cmdline b/casr/tests/casr_tests/casrep/afl-out-sharpfuzz/afl_s01-worker/cmdline new file mode 100644 index 00000000..e5ec54e9 --- /dev/null +++ b/casr/tests/casr_tests/casrep/afl-out-sharpfuzz/afl_s01-worker/cmdline @@ -0,0 +1,3 @@ +/usr/bin/dotnet +/build_test/test_casr_afl_csharp/bin/test_casr_afl_csharp.dll +@@ diff --git a/casr/tests/casr_tests/casrep/afl-out-sharpfuzz/afl_s01-worker/crashes/id:000000,sig:02,src:000001,time:0,execs:10,op:havoc,rep:1 b/casr/tests/casr_tests/casrep/afl-out-sharpfuzz/afl_s01-worker/crashes/id:000000,sig:02,src:000001,time:0,execs:10,op:havoc,rep:1 new file mode 100644 index 00000000..c2c357e3 --- /dev/null +++ b/casr/tests/casr_tests/casrep/afl-out-sharpfuzz/afl_s01-worker/crashes/id:000000,sig:02,src:000001,time:0,execs:10,op:havoc,rep:1 @@ -0,0 +1 @@ +bdax \ No newline at end of file diff --git a/casr/tests/casr_tests/casrep/afl-out-sharpfuzz/afl_s01-worker/crashes/id:000001,sig:02,src:000001,time:1,execs:12,op:havoc,rep:1 b/casr/tests/casr_tests/casrep/afl-out-sharpfuzz/afl_s01-worker/crashes/id:000001,sig:02,src:000001,time:1,execs:12,op:havoc,rep:1 new file mode 100644 index 0000000000000000000000000000000000000000..d21b57de118e99975df8dde5fbb919dc125520e3 GIT binary patch literal 4 LcmZQzNT~n-0Ym`Z literal 0 HcmV?d00001 diff --git a/casr/tests/casr_tests/casrep/afl-out-sharpfuzz/afl_s01-worker/crashes/id:000002,sig:02,src:000001,time:2,execs:24,op:havoc,rep:8 b/casr/tests/casr_tests/casrep/afl-out-sharpfuzz/afl_s01-worker/crashes/id:000002,sig:02,src:000001,time:2,execs:24,op:havoc,rep:8 new file mode 100644 index 00000000..daeedb43 --- /dev/null +++ b/casr/tests/casr_tests/casrep/afl-out-sharpfuzz/afl_s01-worker/crashes/id:000002,sig:02,src:000001,time:2,execs:24,op:havoc,rep:8 @@ -0,0 +1 @@ +ba&&&&€ \ No newline at end of file diff --git a/casr/tests/casr_tests/casrep/afl-out-sharpfuzz/afl_s01-worker/fuzzer_stats b/casr/tests/casr_tests/casrep/afl-out-sharpfuzz/afl_s01-worker/fuzzer_stats new file mode 100644 index 00000000..e134b762 --- /dev/null +++ b/casr/tests/casr_tests/casrep/afl-out-sharpfuzz/afl_s01-worker/fuzzer_stats @@ -0,0 +1,43 @@ +start_time : 1710252309 +last_update : 1710252326 +run_time : 17 +fuzzer_pid : 1466 +cycles_done : 494 +cycles_wo_finds : 494 +time_wo_finds : 0 +execs_done : 197874 +execs_per_sec : 11390.40 +execs_ps_last_min : 0.00 +corpus_count : 2 +corpus_favored : 1 +corpus_found : 0 +corpus_imported : 0 +corpus_variable : 0 +max_depth : 1 +cur_item : 1 +pending_favs : 0 +pending_total : 0 +stability : 100.00% +bitmap_cvg : 0.01% +saved_crashes : 3 +saved_hangs : 0 +last_find : 0 +last_crash : 1710252309 +last_hang : 0 +execs_since_crash : 197850 +exec_timeout : 10000 +slowest_exec_ms : 0 +peak_rss_mb : 36 +cpu_affinity : 1 +edges_found : 6 +total_edges : 65536 +var_byte_count : 0 +havoc_expansion : 5 +auto_dict_entries : 0 +testcache_size : 4 +testcache_count : 1 +testcache_evict : 0 +afl_banner : /build_test/test_casr_afl_csharp/bin/test_casr_afl_csharp.dll#afl_s01 +afl_version : ++4.09c +target_mode : shmem_testcase default +command_line : /usr/local/bin/afl-fuzz -T /build_test/test_casr_afl_csharp/bin/test_casr_afl_csharp.dll#afl_s01 -S afl_s01-worker -i /fuzz/test-out/corpus -t 10000 -o /fuzz/test-out/aflplusplus -a binary -p explore -- /usr/bin/dotnet /build_test/test_casr_afl_csharp/bin/test_casr_afl_csharp.dll @@ diff --git a/casr/tests/casr_tests/csharp/test_casr_afl_csharp/test_casr_afl_csharp.cs b/casr/tests/casr_tests/csharp/test_casr_afl_csharp/test_casr_afl_csharp.cs new file mode 100644 index 00000000..b5cc3e61 --- /dev/null +++ b/casr/tests/casr_tests/csharp/test_casr_afl_csharp/test_casr_afl_csharp.cs @@ -0,0 +1,24 @@ +using SharpFuzz; + +public class Program +{ + public static void Main(string[] args) + { + Fuzzer.OutOfProcess.Run(stream => + { + using (var reader = new StreamReader(args[0])) + { + if (reader.BaseStream.Length < 4) + return; + + string buffer = reader.ReadLine(); + + f1(buffer); + } + }); + } + + public static void f1(string s) { + Module.f2(s); + } +} diff --git a/casr/tests/casr_tests/csharp/test_casr_afl_csharp/test_casr_afl_csharp.csproj b/casr/tests/casr_tests/csharp/test_casr_afl_csharp/test_casr_afl_csharp.csproj new file mode 100644 index 00000000..23b42045 --- /dev/null +++ b/casr/tests/casr_tests/csharp/test_casr_afl_csharp/test_casr_afl_csharp.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + enable + disable + + + + + + + + diff --git a/casr/tests/casr_tests/csharp/test_casr_afl_csharp_module/test_casr_afl_csharp_module.cs b/casr/tests/casr_tests/csharp/test_casr_afl_csharp_module/test_casr_afl_csharp_module.cs new file mode 100644 index 00000000..b3c64eb1 --- /dev/null +++ b/casr/tests/casr_tests/csharp/test_casr_afl_csharp_module/test_casr_afl_csharp_module.cs @@ -0,0 +1,21 @@ +using System; +using System.IO; + +public static class Module { + public static void f2(string s) { + if (s[0] == 'b') + Console.WriteLine("Path 1T"); + else + throw new IndexOutOfRangeException("Index out of range"); + + if (s[1] == 'a') + Console.WriteLine("Path 1T"); + else + throw new ArgumentException("Parameter cannot be null"); + + if (s[2] == 'd') + Console.WriteLine("Path 1T"); + else + throw new System.IO.IOException("IO error"); + } +} diff --git a/casr/tests/casr_tests/csharp/test_casr_afl_csharp_module/test_casr_afl_csharp_module.csproj b/casr/tests/casr_tests/csharp/test_casr_afl_csharp_module/test_casr_afl_csharp_module.csproj new file mode 100644 index 00000000..740c693b --- /dev/null +++ b/casr/tests/casr_tests/csharp/test_casr_afl_csharp_module/test_casr_afl_csharp_module.csproj @@ -0,0 +1,9 @@ + + + + net8.0 + enable + disable + + + diff --git a/casr/tests/tests.rs b/casr/tests/tests.rs index 7dfc8611..361fd527 100644 --- a/casr/tests/tests.rs +++ b/casr/tests/tests.rs @@ -3701,7 +3701,6 @@ fn test_casr_afl() { &paths[1], "--casr-gdb-args", "/tmp/load_sydr", - "--", "@@", ]) .env( @@ -5797,63 +5796,53 @@ fn test_casr_csharp() { #[test] #[cfg(target_arch = "x86_64")] fn test_casr_afl_csharp() { - /*use std::collections::HashMap; + use std::collections::HashMap; - let test_dir = abs_path("tests/tmp_tests_casr/test_casr_libfuzzer_jazzer_js"); - let _ = std::fs::remove_dir_all(test_dir); let paths = [ - abs_path("tests/casr_tests/js/test_casr_libfuzzer_jazzer_js.js"), - abs_path("tests/tmp_tests_casr/test_casr_libfuzzer_jazzer_js/crashes"), - abs_path("tests/tmp_tests_casr/test_casr_libfuzzer_jazzer_js/casr_out"), - abs_path( - "tests/tmp_tests_casr/test_casr_libfuzzer_jazzer_js/test_casr_libfuzzer_jazzer_js.js", - ), + abs_path("tests/casr_tests/casrep/afl-out-sharpfuzz"), + abs_path("tests/tmp_tests_casr/casr_afl_csharp_out"), + abs_path("tests/casr_tests/csharp/test_casr_afl_csharp"), + abs_path("tests/casr_tests/csharp/test_casr_afl_csharp_module"), + abs_path("/tmp/test_casr_afl_csharp"), + abs_path("/tmp/test_casr_afl_csharp_module"), ]; - // Create crashes dir - let output = Command::new("mkdir") - .args(["-p", &paths[1]]) + let _ = fs::remove_dir_all(&paths[1]); + let _ = fs::create_dir(abs_path("tests/tmp_tests_casr")); + let _ = Command::new("cp") + .args(["-r", &paths[2], &paths[4]]) .output() - .expect("failed to create dir"); - assert!( - output.status.success(), - "Stdout {}.\n Stderr: {}", - String::from_utf8_lossy(&output.stdout), - String::from_utf8_lossy(&output.stderr) - ); - - let _ = fs::copy(&paths[0], &paths[3]); - - let Ok(npx_path) = which::which("npx") else { - panic!("No npx is found."); + .expect("failed to copy dir"); + let _ = Command::new("cp") + .args(["-r", &paths[3], &paths[5]]) + .output() + .expect("failed to copy dir"); + let Ok(dotnet_path) = which::which("dotnet") else { + panic!("No dotnet is found."); }; - Command::new("unzip") - .arg(abs_path("tests/casr_tests/js/crashes.zip")) - .args(["-d", &paths[1]]) - .stdout(Stdio::null()) - .status() - .expect("failed to unzip crashes.zip"); + 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", + "--project", + &format!("{}/test_casr_afl_csharp.csproj", &paths[4]), + "@@", + ]) + .env( + "PATH", + format!("{}:{}", bins.display(), std::env::var("PATH").unwrap()), + ); - 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[1], - "-o", - &paths[2], - "--", - npx_path.to_str().unwrap(), - "jazzer", - &paths[3], - ]) - .env( - "PATH", - format!("{}:{}", bins.display(), std::env::var("PATH").unwrap()), - ); - let output = cmd.output().expect("failed to start casr-libfuzzer"); + print!("{:?}", output); + let output = output.output().expect("asfs"); assert!( output.status.success(), @@ -5861,19 +5850,13 @@ fn test_casr_afl_csharp() { String::from_utf8_lossy(&output.stdout), String::from_utf8_lossy(&output.stderr) ); - let err = String::from_utf8_lossy(&output.stderr); - - assert!(!err.is_empty()); + let res = String::from_utf8_lossy(&output.stderr); - assert!(err.contains("NOT_EXPLOITABLE")); - assert!(err.contains("TypeError")); - assert!(err.contains("ReferenceError")); - assert!(err.contains("RangeError")); - assert!(err.contains("test_casr_libfuzzer_jazzer_js.js")); + assert!(!res.is_empty()); let re = Regex::new(r"Number of reports after deduplication: (?P\d+)").unwrap(); let unique_cnt = re - .captures(&err) + .captures(&res) .unwrap() .name("unique") .map(|x| x.as_str()) @@ -5885,7 +5868,7 @@ fn test_casr_afl_csharp() { let re = Regex::new(r"Number of clusters: (?P\d+)").unwrap(); let clusters_cnt = re - .captures(&err) + .captures(&res) .unwrap() .name("clusters") .map(|x| x.as_str()) @@ -5893,10 +5876,10 @@ fn test_casr_afl_csharp() { .parse::() .unwrap(); - assert_eq!(clusters_cnt, 1, "Invalid number of clusters"); + assert_eq!(clusters_cnt, 3, "Invalid number of clusters"); let mut storage: HashMap = HashMap::new(); - for entry in fs::read_dir(&paths[2]).unwrap() { + 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() { @@ -5915,5 +5898,7 @@ fn test_casr_afl_csharp() { } } - assert!(storage.values().all(|x| *x > 1));*/ + assert!(storage.values().all(|x| *x > 1)); + let _ = fs::remove_file(&paths[4]); + let _ = fs::remove_file(&paths[5]); } diff --git a/docs/usage.md b/docs/usage.md index e3af806a..205071e9 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -227,7 +227,7 @@ Create CASR reports (.casrep) from C# reports Run casr-csharp: - $ casr-csharp -o csharp.casrep -- dotnet casr/tests/casr_tests/csharp/test_casr_csharp.dll + $ casr-csharp -o csharp.casrep -- dotnet run --project casr/tests/casr_tests/csharp/test_casr_csharp.csproj ## casr-core @@ -440,8 +440,7 @@ Triage crashes found by AFL++ (Sharpfuzz) Usage: casr-afl [OPTIONS] --input --output [-- ...] Arguments: - [ARGS]... Add "-- ./gdb_fuzz_target " to generate additional crash reports - with casr-gdb (e.g., test whether program crashes without sanitizers) + [ARGS]... Add "-- fuzz_target " Options: -l, --log-level @@ -550,7 +549,7 @@ AFL++ Example (Ubuntu 20.04+): You may also run `casr-afl` with additional report generation for uninstrumented binary with `casr-gdb`: - $ casr-afl -i casr/tests/casr_tests/casrep/afl-out-xlnt -o casr/tests/tmp_tests_casr/casr_afl_out -- /tmp/load_sydr @@ + $ casr-afl -i casr/tests/casr_tests/casrep/afl-out-xlnt -o casr/tests/tmp_tests_casr/casr_afl_out --casr-gdb-args /tmp/load_sydr @@ Thus, `casr-afl` will generate GDB crash report for each unique ASAN crash. So, you can estimate crash severity for program built without sanitizers. @@ -558,10 +557,11 @@ you can estimate crash severity for program built without sanitizers. You can set environment variable `RUST_BACKTRACE=(1|full)` for `casr-afl`. This variable may be used by [casr-san](#casr-san). -Sharpfuzz example: +`casr-afl` example for AFL++-based fuzzer `Sharpfuzz`: - $ ... - $ ... + $ $ cp casr/tests/casr_tests/csharp/test_casr_afl_csharp /tmp/test_casr_afl_csharp + $ cp casr/tests/casr_tests/csharp/test_casr_afl_csharp_module /tmp/test_casr_afl_csharp_module + $ casr-afl -i casr/tests/casr_tests/casrep/afl-out-sharpfuzz -o casr/tests/tmp_tests_casr/casr_afl_csharp_out -- dotnet run --project /tmp/test_casr_afl_csharp/test_casr_afl_csharp.csproj @@ ## casr-libfuzzer From 58a62cb1a789425f0579323947bd86e08770695e Mon Sep 17 00:00:00 2001 From: headshog Date: Tue, 12 Mar 2024 19:11:31 +0300 Subject: [PATCH 13/38] tests and casr-afl done x2 --- casr/src/bin/casr-afl.rs | 5 ++--- casr/src/bin/casr-libfuzzer.rs | 7 +++---- casr/tests/tests.rs | 3 +-- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/casr/src/bin/casr-afl.rs b/casr/src/bin/casr-afl.rs index 7b4d4bab..5abd0eb7 100644 --- a/casr/src/bin/casr-afl.rs +++ b/casr/src/bin/casr-afl.rs @@ -99,7 +99,6 @@ fn main() -> Result<()> { Arg::new("casr-gdb-args") .long("casr-gdb-args") .action(ArgAction::Set) - .num_args(1..) .help("Add \"--casr-gdb-args \'./gdb_fuzz_target \'\" to generate additional crash reports with casr-gdb (e.g., test whether program crashes without sanitizers)"), ) .arg( @@ -122,8 +121,8 @@ fn main() -> Result<()> { }; // Get gdb args. - let mut gdb_args = if let Some(argv) = matches.get_many::("casr-gdb-args") { - argv.cloned().collect() + let mut gdb_args = if let Some(argv) = matches.get_one::("casr-gdb-args") { + shell_words::split(argv)? } else { Vec::new() }; diff --git a/casr/src/bin/casr-libfuzzer.rs b/casr/src/bin/casr-libfuzzer.rs index b2d7f8f2..1e61b000 100644 --- a/casr/src/bin/casr-libfuzzer.rs +++ b/casr/src/bin/casr-libfuzzer.rs @@ -91,7 +91,6 @@ fn main() -> Result<()> { Arg::new("casr-gdb-args") .long("casr-gdb-args") .action(ArgAction::Set) - .num_args(1..) .help("Add \"--casr-gdb-args \'./gdb_fuzz_target \'\" to generate additional crash reports with casr-gdb (e.g., test whether program crashes without sanitizers)"), ) .arg( @@ -118,8 +117,8 @@ fn main() -> Result<()> { }; // Get gdb args. - let gdb_args = if let Some(argv) = matches.get_many::("casr-gdb-args") { - argv.cloned().collect() + let gdb_args = if let Some(argv) = matches.get_one::("casr-gdb-args") { + shell_words::split(argv)? } else { Vec::new() }; @@ -147,7 +146,7 @@ fn main() -> Result<()> { }; let tool_path = util::get_path(tool)?; - if !gdb_args.is_empty() && tool != "casr-gdb" { + if !gdb_args.is_empty() && tool != "casr-gdb" && tool != "casr-san" { bail!("casr-gdb-args is provided with other tool (casr-python or casr-java or casr-js)."); } diff --git a/casr/tests/tests.rs b/casr/tests/tests.rs index 361fd527..f3427593 100644 --- a/casr/tests/tests.rs +++ b/casr/tests/tests.rs @@ -3700,8 +3700,7 @@ fn test_casr_afl() { "-o", &paths[1], "--casr-gdb-args", - "/tmp/load_sydr", - "@@", + "/tmp/load_sydr @@", ]) .env( "PATH", From 8e22058543592694a1f5b8aeba48e5ac5110b4a0 Mon Sep 17 00:00:00 2001 From: headshog Date: Tue, 12 Mar 2024 19:14:57 +0300 Subject: [PATCH 14/38] remove artifact --- abc | 318 ------------------------------------------------------------ 1 file changed, 318 deletions(-) delete mode 100644 abc diff --git a/abc b/abc deleted file mode 100644 index 87a13562..00000000 --- a/abc +++ /dev/null @@ -1,318 +0,0 @@ -diff --git a/casr/src/bin/casr-afl.rs b/casr/src/bin/casr-afl.rs -index 5e98fa6..81d9269 100644 ---- a/casr/src/bin/casr-afl.rs -+++ b/casr/src/bin/casr-afl.rs -@@ -1,13 +1,13 @@ - use casr::triage::{fuzzing_crash_triage_pipeline, CrashInfo}; - use casr::util; - -+use anyhow::bail; - use anyhow::Result; - use clap::{ - error::{ContextKind, ContextValue, ErrorKind}, - Arg, ArgAction, - }; - use log::error; --use anyhow::bail; - - use std::collections::HashMap; - use std::fs; -@@ -132,20 +132,17 @@ fn main() -> Result<()> { - }; - - let at_index = if gdb_args.is_empty() { -- print!("here {:?}\n", argv); - if let Some(idx) = argv.iter().skip(1).position(|s| s.contains("@@")) { - Some(idx + 1) - } else { - argv.push("@@"); - Some(argv.len() - 1) - } -+ } else if !argv.is_empty() && argv[0] == "@@" { -+ gdb_args.push("@@".to_string()); -+ Some(gdb_args.len() - 1) - } else { -- if !argv.is_empty() && argv[0] == "@@" { -- gdb_args.push("@@".to_string()); -- Some(gdb_args.len() - 1) -- } else { -- None -- } -+ None - }; - - // Get tool. -@@ -193,7 +190,7 @@ fn main() -> Result<()> { - ] - .into_iter() - .collect(), -- at_index: at_index, -+ at_index, - ..Default::default() - } - }; -diff --git a/casr/src/bin/casr-csharp.rs b/casr/src/bin/casr-csharp.rs -index 516497e..d467929 100644 ---- a/casr/src/bin/casr-csharp.rs -+++ b/casr/src/bin/casr-csharp.rs -@@ -105,10 +105,10 @@ fn main() -> Result<()> { - // Set executable path (for C# .dll (dotnet) or .exe (mono) file) - if let Some(pos) = argv - .iter() -- .position(|x| x.ends_with(".dll") || x.ends_with(".exe")) -+ .position(|x| x.ends_with(".dll") || x.ends_with(".exe") || x.ends_with(".csproj")) - { - let Some(classes) = argv.get(pos) else { -- bail!("dotnet target is not specified by .dll executable."); -+ bail!("dotnet target is not specified by .dll, .exe or .csproj executable."); - }; - report.executable_path = classes.to_string(); - } -diff --git a/casr/tests/casr_tests/csharp/test_casr_csharp.cs b/casr/tests/casr_tests/csharp/test_casr_csharp.cs -new file mode 100644 -index 0000000..9ff4fa3 ---- /dev/null -+++ b/casr/tests/casr_tests/csharp/test_casr_csharp.cs -@@ -0,0 +1,17 @@ -+using System; -+ -+public class Program -+{ -+ public static void Main(string[] args) -+ { -+ f1(); -+ } -+ -+ public static void f1() { -+ f2(); -+ } -+ -+ public static void f2() { -+ throw new ArgumentException("Parameter cannot be null"); -+ } -+} -diff --git a/casr/tests/casr_tests/csharp/test_casr_csharp.csproj b/casr/tests/casr_tests/csharp/test_casr_csharp.csproj -new file mode 100644 -index 0000000..206b89a ---- /dev/null -+++ b/casr/tests/casr_tests/csharp/test_casr_csharp.csproj -@@ -0,0 +1,10 @@ -+ -+ -+ -+ Exe -+ net8.0 -+ enable -+ enable -+ -+ -+ -diff --git a/casr/tests/tests.rs b/casr/tests/tests.rs -index 0a16fe5..c4c4047 100644 ---- a/casr/tests/tests.rs -+++ b/casr/tests/tests.rs -@@ -22,6 +22,7 @@ lazy_static::lazy_static! { - static ref EXE_CASR_PYTHON: RwLock<&'static str> = RwLock::new(env!("CARGO_BIN_EXE_casr-python")); - static ref EXE_CASR_JAVA: RwLock<&'static str> = RwLock::new(env!("CARGO_BIN_EXE_casr-java")); - static ref EXE_CASR_JS: RwLock<&'static str> = RwLock::new(env!("CARGO_BIN_EXE_casr-js")); -+ static ref EXE_CASR_CSHARP: RwLock<&'static str> = RwLock::new(env!("CARGO_BIN_EXE_casr-csharp")); - static ref EXE_CASR_GDB: RwLock<&'static str> = RwLock::new(env!("CARGO_BIN_EXE_casr-gdb")); - static ref PROJECT_DIR: RwLock<&'static str> = RwLock::new(env!("CARGO_MANIFEST_DIR")); - } -@@ -5705,3 +5706,183 @@ fn test_casr_libfuzzer_jazzer_js_xml2js() { - - assert!(storage.values().all(|x| *x > 1)); - } -+ -+#[test] -+#[cfg(target_arch = "x86_64")] -+fn test_casr_csharp() { -+ let paths = [ -+ abs_path("tests/casr_tests/csharp/test_casr_csharp.cs"), -+ abs_path("tests/casr_tests/csharp/test_casr_csharp.csproj"), -+ abs_path("tests/tmp_tests_casr/test_casr_csharp"), -+ abs_path("tests/tmp_tests_casr/test_casr_csharp/test_casr_csharp.cs"), -+ abs_path("tests/tmp_tests_casr/test_casr_csharp/test_casr_csharp.csproj"), -+ ]; -+ let _ = std::fs::create_dir_all(&paths[2]); -+ let _ = fs::copy(&paths[0], &paths[3]); -+ let _ = fs::copy(&paths[1], &paths[4]); -+ let Ok(dotnet_path) = which::which("dotnet") else { -+ panic!("No dotnet is found."); -+ }; -+ -+ let output = Command::new(*EXE_CASR_CSHARP.read().unwrap()) -+ .args([ -+ "--stdout", -+ "--", -+ &dotnet_path.to_str().unwrap(), -+ "run", -+ "--project", -+ &paths[4], -+ ]) -+ .output() -+ .expect("failed to start casr-csharp"); -+ -+ assert!( -+ output.status.success(), -+ "Stdout {}.\n Stderr: {}", -+ String::from_utf8_lossy(&output.stdout), -+ String::from_utf8_lossy(&output.stderr) -+ ); -+ -+ let report: Result = serde_json::from_slice(&output.stdout); -+ if let Ok(report) = report { -+ let severity_type = report["CrashSeverity"]["Type"].as_str().unwrap(); -+ let severity_desc = report["CrashSeverity"]["ShortDescription"] -+ .as_str() -+ .unwrap() -+ .to_string(); -+ -+ assert_eq!(3, report["Stacktrace"].as_array().unwrap().iter().count()); -+ assert_eq!(severity_type, "UNDEFINED"); -+ assert_eq!(severity_desc, "System.ArgumentException"); -+ assert!(report["CrashLine"] -+ .as_str() -+ .unwrap() -+ .contains("test_casr_csharp.cs:15")); -+ } else { -+ panic!("Couldn't parse json report file."); -+ } -+} -+ -+#[test] -+#[cfg(target_arch = "x86_64")] -+fn test_casr_afl_csharp() { -+ /*use std::collections::HashMap; -+ -+ let test_dir = abs_path("tests/tmp_tests_casr/test_casr_libfuzzer_jazzer_js"); -+ let _ = std::fs::remove_dir_all(test_dir); -+ let paths = [ -+ abs_path("tests/casr_tests/js/test_casr_libfuzzer_jazzer_js.js"), -+ abs_path("tests/tmp_tests_casr/test_casr_libfuzzer_jazzer_js/crashes"), -+ abs_path("tests/tmp_tests_casr/test_casr_libfuzzer_jazzer_js/casr_out"), -+ abs_path( -+ "tests/tmp_tests_casr/test_casr_libfuzzer_jazzer_js/test_casr_libfuzzer_jazzer_js.js", -+ ), -+ ]; -+ -+ // Create crashes dir -+ let output = Command::new("mkdir") -+ .args(["-p", &paths[1]]) -+ .output() -+ .expect("failed to create dir"); -+ assert!( -+ output.status.success(), -+ "Stdout {}.\n Stderr: {}", -+ String::from_utf8_lossy(&output.stdout), -+ String::from_utf8_lossy(&output.stderr) -+ ); -+ -+ let _ = fs::copy(&paths[0], &paths[3]); -+ -+ let Ok(npx_path) = which::which("npx") else { -+ panic!("No npx is found."); -+ }; -+ -+ Command::new("unzip") -+ .arg(abs_path("tests/casr_tests/js/crashes.zip")) -+ .args(["-d", &paths[1]]) -+ .stdout(Stdio::null()) -+ .status() -+ .expect("failed to unzip crashes.zip"); -+ -+ 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[1], -+ "-o", -+ &paths[2], -+ "--", -+ npx_path.to_str().unwrap(), -+ "jazzer", -+ &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("NOT_EXPLOITABLE")); -+ assert!(err.contains("TypeError")); -+ assert!(err.contains("ReferenceError")); -+ assert!(err.contains("RangeError")); -+ assert!(err.contains("test_casr_libfuzzer_jazzer_js.js")); -+ -+ let re = Regex::new(r"Number of reports after deduplication: (?P\d+)").unwrap(); -+ let unique_cnt = re -+ .captures(&err) -+ .unwrap() -+ .name("unique") -+ .map(|x| x.as_str()) -+ .unwrap() -+ .parse::() -+ .unwrap(); -+ -+ assert_eq!(unique_cnt, 3, "Invalid number of deduplicated reports"); -+ -+ let re = Regex::new(r"Number of clusters: (?P\d+)").unwrap(); -+ let clusters_cnt = re -+ .captures(&err) -+ .unwrap() -+ .name("clusters") -+ .map(|x| x.as_str()) -+ .unwrap() -+ .parse::() -+ .unwrap(); -+ -+ assert_eq!(clusters_cnt, 1, "Invalid number of clusters"); -+ -+ let mut storage: HashMap = HashMap::new(); -+ for entry in fs::read_dir(&paths[2]).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));*/ -+} -diff --git a/libcasr/src/execution_class.rs b/libcasr/src/execution_class.rs -index ab69db5..91f6e76 100644 ---- a/libcasr/src/execution_class.rs -+++ b/libcasr/src/execution_class.rs -@@ -204,7 +204,7 @@ impl Default for ExecutionClass { - severity: "UNDEFINED".to_string(), - short_description: "Undefined".to_string(), - description: "Undefined class".to_string(), -- explanation: "The is no execution class for this type of exception".to_string(), -+ explanation: "There is no execution class for this type of exception".to_string(), - } - } - } From 2f0a243804df507656301be565da97b423b20eaf Mon Sep 17 00:00:00 2001 From: headshog Date: Wed, 13 Mar 2024 13:02:31 +0300 Subject: [PATCH 15/38] fix casr-afl anc docs --- README.md | 12 +++++------- casr/src/bin/casr-afl.rs | 4 ++-- docs/usage.md | 3 +-- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 810d1645..d29075c1 100644 --- a/README.md +++ b/README.md @@ -35,8 +35,7 @@ java reports and get report from to analyze JavaScript reports and get report from [Jazzer.js](https://github.com/CodeIntelligenceTesting/jazzer.js) or [jsfuzz](https://github.com/fuzzitdev/jsfuzz). -Use `casr-csharp` to analyze C# reports and get report from -[Sharpfuzz](https://github.com/Metalnem/sharpfuzz). +Use `casr-csharp` to analyze C# reports. Crash report contains many useful information: severity (like [exploitable](https://github.com/jfoote/exploitable)) for x86, x86\_64, arm32, aarch64, rv32g, rv64g architectures, @@ -167,7 +166,7 @@ Create report from JavaScript: Create report from C#: - $ casr-csharp -o csharp.casrep -- dotnet run --project casr/tests/casr_tests/csharp/bin/test_casr_csharp.csproj + $ casr-csharp -o csharp.casrep -- dotnet run --project casr/tests/casr_tests/csharp/test_casr_csharp.csproj View report: @@ -199,7 +198,7 @@ Triage crashes after AFL++ fuzzing with casr-afl: $ cp casr/tests/casr_tests/bin/load_sydr /tmp/load_sydr $ casr-afl -i casr/tests/casr_tests/casrep/afl-out-xlnt -o casr/tests/tmp_tests_casr/casr_afl_out $ # You may also additionally generate crash reports for uninstrumented binary with casr-gdb - $ casr-afl -i casr/tests/casr_tests/casrep/afl-out-xlnt -o casr/tests/tmp_tests_casr/casr_afl_out -- /tmp/load_sydr @@ + $ casr-afl -i casr/tests/casr_tests/casrep/afl-out-xlnt -o casr/tests/tmp_tests_casr/casr_afl_out --casr-gdb-args /tmp/load_sydr @@ Triage crashes after Sharpfuzz fuzzing with casr-afl: @@ -249,7 +248,8 @@ When you have crashes from fuzzing you may do the following steps: [DefectDojo](https://github.com/DefectDojo/django-DefectDojo) with `casr-dojo`. -If you use [AFL++](https://github.com/AFLplusplus/AFLplusplus), the pipeline +If you use [AFL++](https://github.com/AFLplusplus/AFLplusplus) or AFL-based +fuzzer [Sharpfuzz](https://www.llvm.org/docs/LibFuzzer.html), the pipeline (without `casr-ubsan` and `casr-dojo`) could be done automatically by `casr-afl`. @@ -259,8 +259,6 @@ If you use [libFuzzer](https://www.llvm.org/docs/LibFuzzer.html) based fuzzer [jsfuzz](https://github.com/fuzzitdev/jsfuzz)), the pipeline (without `casr-ubsan` and `casr-dojo`) could be done automatically by `casr-libfuzzer`. -If you use [Sharpfuzz](https://www.llvm.org/docs/LibFuzzer.html), the pipeline (without `casr-ubsan` and `casr-dojo`) could be done automatically by `casr-afl`. - ## Contributing Feel free to open [issues](https://github.com/ispras/casr/issues) or [PRs](https://github.com/ispras/casr/pulls) (especially pay attention to [help wanted](https://github.com/ispras/casr/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22) issues)! We appreciate your support! diff --git a/casr/src/bin/casr-afl.rs b/casr/src/bin/casr-afl.rs index 5abd0eb7..894f81ea 100644 --- a/casr/src/bin/casr-afl.rs +++ b/casr/src/bin/casr-afl.rs @@ -165,7 +165,7 @@ fn main() -> Result<()> { } // Get crashes from one node. - let mut crash_info = if !gdb_args.is_empty() { + let mut crash_info = if tool == "casr-gdb" { CrashInfo { target_args: if matches.get_flag("ignore-cmdline") { gdb_args.clone() @@ -200,7 +200,7 @@ fn main() -> Result<()> { crash_info.casr_tool = tool_path.clone(); // When we triage crashes for binaries, use casr-san. - if !gdb_args.is_empty() { + if tool == "casr-gdb" { crash_info.at_index = crash_info .target_args .iter() diff --git a/docs/usage.md b/docs/usage.md index 205071e9..e451616c 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -10,8 +10,7 @@ java reports and get report from to analyze JavaScript reports and get report from [Jazzer.js](https://github.com/CodeIntelligenceTesting/jazzer.js) or [jsfuzz](https://github.com/fuzzitdev/jsfuzz). -Use `casr-csharp` to analyze C# reports and get report from -[Sharpfuzz](https://github.com/Metalnem/sharpfuzz). `casr-afl` can triage crashes +Use `casr-csharp` to analyze C# reports. `casr-afl` can triage crashes found by [AFL++](https://github.com/AFLplusplus/AFLplusplus) (Sharpfuzz). `casr-libfuzzer` can triage crashes found by [libFuzzer](https://www.llvm.org/docs/LibFuzzer.html) (libFuzzer, go-fuzz, From 4275d45b77d3f8d28fb364df257ffc7fc5e520dd Mon Sep 17 00:00:00 2001 From: headshog Date: Wed, 13 Mar 2024 13:27:24 +0300 Subject: [PATCH 16/38] fix ARGS in code and docs --- casr/src/bin/casr-csharp.rs | 1 + casr/src/bin/casr-gdb.rs | 1 + casr/src/bin/casr-java.rs | 1 + casr/src/bin/casr-js.rs | 1 + casr/src/bin/casr-python.rs | 1 + casr/src/bin/casr-san.rs | 1 + casr/src/bin/casr-ubsan.rs | 1 + casr/tests/tests.rs | 10 ++++++++-- docs/usage.md | 16 ++++++++-------- 9 files changed, 23 insertions(+), 10 deletions(-) diff --git a/casr/src/bin/casr-csharp.rs b/casr/src/bin/casr-csharp.rs index d4679296..37f0fdbe 100644 --- a/casr/src/bin/casr-csharp.rs +++ b/casr/src/bin/casr-csharp.rs @@ -67,6 +67,7 @@ fn main() -> Result<()> { .action(ArgAction::Set) .num_args(1..) .last(true) + .required(true) .help("Add \"-- \" to run"), ) .get_matches(); diff --git a/casr/src/bin/casr-gdb.rs b/casr/src/bin/casr-gdb.rs index 013423b8..51fddf8e 100644 --- a/casr/src/bin/casr-gdb.rs +++ b/casr/src/bin/casr-gdb.rs @@ -92,6 +92,7 @@ fn main() -> Result<()> { .action(ArgAction::Set) .num_args(1..) .last(true) + .required(true) .help("Add \"-- ./binary \" to run executable"), ) .get_matches(); diff --git a/casr/src/bin/casr-java.rs b/casr/src/bin/casr-java.rs index 2f544657..71e4fe66 100644 --- a/casr/src/bin/casr-java.rs +++ b/casr/src/bin/casr-java.rs @@ -87,6 +87,7 @@ fn main() -> Result<()> { .action(ArgAction::Set) .num_args(1..) .last(true) + .required(true) .help("Add \"-- \" to run"), ) .get_matches(); diff --git a/casr/src/bin/casr-js.rs b/casr/src/bin/casr-js.rs index 8ac68fad..96acc8c7 100644 --- a/casr/src/bin/casr-js.rs +++ b/casr/src/bin/casr-js.rs @@ -75,6 +75,7 @@ fn main() -> Result<()> { .action(ArgAction::Set) .num_args(1..) .last(true) + .required(true) .help("Add \"-- \" to run"), ) .get_matches(); diff --git a/casr/src/bin/casr-python.rs b/casr/src/bin/casr-python.rs index b7ace1bf..70b3e0ef 100644 --- a/casr/src/bin/casr-python.rs +++ b/casr/src/bin/casr-python.rs @@ -79,6 +79,7 @@ fn main() -> Result<()> { .action(ArgAction::Set) .num_args(1..) .last(true) + .required(true) .help("Add \"-- \" to run"), ) .get_matches(); diff --git a/casr/src/bin/casr-san.rs b/casr/src/bin/casr-san.rs index bb20f660..bd85cf44 100644 --- a/casr/src/bin/casr-san.rs +++ b/casr/src/bin/casr-san.rs @@ -95,6 +95,7 @@ fn main() -> Result<()> { .action(ArgAction::Set) .num_args(1..) .last(true) + .required(true) .help("Add \"-- ./binary \" to run executable"), ) .get_matches(); diff --git a/casr/src/bin/casr-ubsan.rs b/casr/src/bin/casr-ubsan.rs index 00d1b483..ffc1e1a7 100644 --- a/casr/src/bin/casr-ubsan.rs +++ b/casr/src/bin/casr-ubsan.rs @@ -287,6 +287,7 @@ fn main() -> Result<()> { .required(false) .num_args(1..) .last(true) + .required(true) .help("Add \"-- \" to run"), ) .get_matches(); diff --git a/casr/tests/tests.rs b/casr/tests/tests.rs index f3427593..c0d4639c 100644 --- a/casr/tests/tests.rs +++ b/casr/tests/tests.rs @@ -5898,6 +5898,12 @@ fn test_casr_afl_csharp() { } assert!(storage.values().all(|x| *x > 1)); - let _ = fs::remove_file(&paths[4]); - let _ = fs::remove_file(&paths[5]); + let _ = Command::new("rm") + .args(["-rf", &paths[4]]) + .output() + .expect("failed to remove dir"); + let _ = Command::new("rm") + .args(["-rf", &paths[5]]) + .output() + .expect("failed to remove dir"); } diff --git a/docs/usage.md b/docs/usage.md index e451616c..d0f614d7 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -23,7 +23,7 @@ SARIF report. Reports triage (deduplication, clustering) is done by `casr-cluste Create CASR reports (.casrep) from gdb execution - Usage: casr-gdb [OPTIONS] <--stdout|--output > [-- ...] + Usage: casr-gdb [OPTIONS] <--stdout|--output > -- ... Arguments: [ARGS]... Add "-- ./binary " to run executable @@ -50,7 +50,7 @@ Example: Create CASR reports (.casrep) from AddressSanitizer reports - Usage: casr-san [OPTIONS] <--stdout|--output > [-- ...] + Usage: casr-san [OPTIONS] <--stdout|--output > -- ... Arguments: [ARGS]... Add "-- ./binary " to run executable @@ -88,7 +88,7 @@ ASAN stacktrace or Rust backtrace to analyze. If environment variable Triage errors found by UndefinedBehaviorSanitizer and create CASR reports (.casrep) - Usage: casr-ubsan [OPTIONS] --input ... --output [-- ...] + Usage: casr-ubsan [OPTIONS] --input ... --output -- ... Arguments: [ARGS]... Add "-- " to run @@ -125,7 +125,7 @@ deduplication to remove equal ubsan errors, then run report generation. Create CASR reports (.casrep) from python reports - Usage: casr-python [OPTIONS] <--stdout|--output > [-- ...] + Usage: casr-python [OPTIONS] <--stdout|--output > -- ... Arguments: [ARGS]... Add "-- " to run @@ -151,7 +151,7 @@ Example: Create CASR reports (.casrep) from java reports - Usage: casr-java [OPTIONS] <--stdout|--output > [-- ...] + Usage: casr-java [OPTIONS] <--stdout|--output > -- ... Arguments: [ARGS]... Add "-- " to run @@ -180,7 +180,7 @@ Run casr-java: Create CASR reports (.casrep) from JavaScript crash reports - Usage: casr-js [OPTIONS] <--stdout|--output > [-- ...] + Usage: casr-js [OPTIONS] <--stdout|--output > -- ... Arguments: [ARGS]... Add "-- " to run @@ -207,7 +207,7 @@ Run casr-js: Create CASR reports (.casrep) from C# reports - Usage: casr-csharp [OPTIONS] <--stdout|--output > [-- ...] + Usage: casr-csharp [OPTIONS] <--stdout|--output > -- ... Arguments: [ARGS]... Add "-- " to run @@ -567,7 +567,7 @@ variable may be used by [casr-san](#casr-san). Triage crashes found by libFuzzer based fuzzer (C/C++/go-fuzz/Atheris/Jazzer/Jazzer.js/jsfuzz) - Usage: casr-libfuzzer [OPTIONS] --output [-- ...] + Usage: casr-libfuzzer [OPTIONS] --output -- ... Arguments: [ARGS]... Add "-- ./fuzz_target " From b498319af06bd7fa69d7110bd84d9d74aefeb116 Mon Sep 17 00:00:00 2001 From: headshog Date: Wed, 13 Mar 2024 13:35:09 +0300 Subject: [PATCH 17/38] fix style --- .../test_casr_afl_csharp.cs | 33 ++++++++----------- .../casr_tests/csharp/test_casr_csharp.cs | 23 +++++++------ docs/usage.md | 2 +- 3 files changed, 26 insertions(+), 32 deletions(-) diff --git a/casr/tests/casr_tests/csharp/test_casr_afl_csharp/test_casr_afl_csharp.cs b/casr/tests/casr_tests/csharp/test_casr_afl_csharp/test_casr_afl_csharp.cs index b5cc3e61..25de400b 100644 --- a/casr/tests/casr_tests/csharp/test_casr_afl_csharp/test_casr_afl_csharp.cs +++ b/casr/tests/casr_tests/csharp/test_casr_afl_csharp/test_casr_afl_csharp.cs @@ -1,24 +1,19 @@ using SharpFuzz; -public class Program -{ - public static void Main(string[] args) - { - Fuzzer.OutOfProcess.Run(stream => - { - using (var reader = new StreamReader(args[0])) - { - if (reader.BaseStream.Length < 4) - return; +public class Program { + public static void Main(string[] args) { + Fuzzer.OutOfProcess.Run(stream => { + using (var reader = new StreamReader(args[0])) { + if (reader.BaseStream.Length < 4) + return; - string buffer = reader.ReadLine(); + string buffer = reader.ReadLine(); + f1(buffer); + } + }); + } - f1(buffer); - } - }); - } - - public static void f1(string s) { - Module.f2(s); - } + public static void f1(string s) { + Module.f2(s); + } } diff --git a/casr/tests/casr_tests/csharp/test_casr_csharp.cs b/casr/tests/casr_tests/csharp/test_casr_csharp.cs index 9ff4fa3e..ac610b61 100644 --- a/casr/tests/casr_tests/csharp/test_casr_csharp.cs +++ b/casr/tests/casr_tests/csharp/test_casr_csharp.cs @@ -1,17 +1,16 @@ using System; -public class Program -{ - public static void Main(string[] args) - { - f1(); - } +public class Program { + public static void Main(string[] args) + { + f1(); + } - public static void f1() { - f2(); - } + public static void f1() { + f2(); + } - public static void f2() { - throw new ArgumentException("Parameter cannot be null"); - } + public static void f2() { + throw new ArgumentException("Parameter cannot be null"); + } } diff --git a/docs/usage.md b/docs/usage.md index d0f614d7..43679ad1 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -556,7 +556,7 @@ you can estimate crash severity for program built without sanitizers. You can set environment variable `RUST_BACKTRACE=(1|full)` for `casr-afl`. This variable may be used by [casr-san](#casr-san). -`casr-afl` example for AFL++-based fuzzer `Sharpfuzz`: +Example for AFL++-based fuzzer `Sharpfuzz`: $ $ cp casr/tests/casr_tests/csharp/test_casr_afl_csharp /tmp/test_casr_afl_csharp $ cp casr/tests/casr_tests/csharp/test_casr_afl_csharp_module /tmp/test_casr_afl_csharp_module From cc7d46471f333702110b61e4dede3e363e504e6c Mon Sep 17 00:00:00 2001 From: headshog Date: Wed, 13 Mar 2024 14:02:38 +0300 Subject: [PATCH 18/38] fix style x2 --- casr/src/bin/casr-afl.rs | 17 +++++------------ casr/src/bin/casr-libfuzzer.rs | 2 +- casr/tests/tests.rs | 2 +- 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/casr/src/bin/casr-afl.rs b/casr/src/bin/casr-afl.rs index 894f81ea..ea7e9059 100644 --- a/casr/src/bin/casr-afl.rs +++ b/casr/src/bin/casr-afl.rs @@ -143,18 +143,11 @@ fn main() -> Result<()> { } // Get input file argument index. - let at_index = if gdb_args.is_empty() { - argv.iter() - .skip(1) - .position(|s| s.contains("@@")) - .map(|x| x + 1) - } else { - gdb_args - .iter() - .skip(1) - .position(|s| s.contains("@@")) - .map(|x| x + 1) - }; + let at_index = argv + .iter() + .skip(1) + .position(|s| s.contains("@@")) + .map(|x| x + 1); // Get all crashes. let mut crashes: HashMap = HashMap::new(); diff --git a/casr/src/bin/casr-libfuzzer.rs b/casr/src/bin/casr-libfuzzer.rs index 1e61b000..ae5d56fe 100644 --- a/casr/src/bin/casr-libfuzzer.rs +++ b/casr/src/bin/casr-libfuzzer.rs @@ -147,7 +147,7 @@ fn main() -> Result<()> { let tool_path = util::get_path(tool)?; if !gdb_args.is_empty() && tool != "casr-gdb" && tool != "casr-san" { - bail!("casr-gdb-args is provided with other tool (casr-python or casr-java or casr-js)."); + bail!("casr-gdb-args option is provided with incompatible tool (casr-python, casr-java or casr-js)."); } // Get input file argument index. diff --git a/casr/tests/tests.rs b/casr/tests/tests.rs index c0d4639c..e1e84a4b 100644 --- a/casr/tests/tests.rs +++ b/casr/tests/tests.rs @@ -5786,7 +5786,7 @@ fn test_casr_csharp() { assert!(report["CrashLine"] .as_str() .unwrap() - .contains("test_casr_csharp.cs:15")); + .contains("test_casr_csharp.cs:14")); } else { panic!("Couldn't parse json report file."); } From 381701735491e8540588045576d300b82a0978c8 Mon Sep 17 00:00:00 2001 From: headshog Date: Wed, 13 Mar 2024 14:10:52 +0300 Subject: [PATCH 19/38] fix style x3 --- casr/src/bin/casr-afl.rs | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/casr/src/bin/casr-afl.rs b/casr/src/bin/casr-afl.rs index ea7e9059..b7b8db96 100644 --- a/casr/src/bin/casr-afl.rs +++ b/casr/src/bin/casr-afl.rs @@ -142,13 +142,6 @@ fn main() -> Result<()> { bail!("casr-gdb-args is provided with other tool (casr-csharp)."); } - // Get input file argument index. - let at_index = argv - .iter() - .skip(1) - .position(|s| s.contains("@@")) - .map(|x| x + 1); - // Get all crashes. let mut crashes: HashMap = HashMap::new(); for node_dir in fs::read_dir(matches.get_one::("input").unwrap())? { @@ -186,21 +179,19 @@ fn main() -> Result<()> { ] .into_iter() .collect(), - at_index, ..Default::default() } }; crash_info.casr_tool = tool_path.clone(); + crash_info.at_index = crash_info + .target_args + .iter() + .skip(1) + .position(|s| s.contains("@@")) + .map(|x| x + 1); // When we triage crashes for binaries, use casr-san. if tool == "casr-gdb" { - crash_info.at_index = crash_info - .target_args - .iter() - .skip(1) - .position(|s| s.contains("@@")) - .map(|x| x + 1); - if let Some(target) = crash_info.target_args.first() { match util::symbols_list(Path::new(target)) { Ok(list) => { From 9fea237b863e99fbc5eb694bb1a7b77d6304713e Mon Sep 17 00:00:00 2001 From: headshog Date: Wed, 13 Mar 2024 14:16:12 +0300 Subject: [PATCH 20/38] fix docs --- docs/usage.md | 74 +++++++++++++++++++++++++-------------------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/docs/usage.md b/docs/usage.md index 43679ad1..60e0459b 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -213,16 +213,16 @@ Create CASR reports (.casrep) from C# reports [ARGS]... Add "-- " to run Options: - -o, --output Path to save report. Path can be a directory, then report name - is generated - --stdout Print CASR report to stdout - --stdin Stdin file for program - -t, --timeout Timeout (in seconds) for target execution, 0 value means that - timeout is disabled [default: 0] - --ignore File with regular expressions for functions and file paths that - should be ignored - -h, --help Print help - -V, --version Print version + -o, --output Path to save report. Path can be a directory, then report name + is generated + --stdout Print CASR report to stdout + --stdin Stdin file for program + -t, --timeout Timeout (in seconds) for target execution, 0 value means that + timeout is disabled [default: 0] + --ignore File with regular expressions for functions and file paths that + should be ignored + -h, --help Print help + -V, --version Print version Run casr-csharp: @@ -442,32 +442,32 @@ Triage crashes found by AFL++ (Sharpfuzz) [ARGS]... Add "-- fuzz_target " Options: - -l, --log-level - Logging level [default: info] [possible values: info, debug] - -j, --jobs - Number of parallel jobs for generating CASR reports [default: half of cpu cores] - -t, --timeout - Timeout (in seconds) for target execution, 0 value means that timeout is - disabled [default: 0] - -i, --input - AFL++ work directory - -o, --output - Output directory with triaged reports - -f, --force-remove - Remove output project directory if it exists - --ignore-cmdline - Force usage to run target instead of searching for cmdline files - in AFL fuzzing directory - --no-cluster - Do not cluster CASR reports - --casr-gdb-args - Add "--casr-gdb-args './gdb_fuzz_target '" to generate additional - crash reports with casr-gdb (e.g., test whether program crashes without - sanitizers) - -h, --help - Print help - -V, --version - Print version + -l, --log-level + Logging level [default: info] [possible values: info, debug] + -j, --jobs + Number of parallel jobs for generating CASR reports [default: half of cpu cores] + -t, --timeout + Timeout (in seconds) for target execution, 0 value means that timeout is + disabled [default: 0] + -i, --input + AFL++ work directory + -o, --output + Output directory with triaged reports + -f, --force-remove + Remove output project directory if it exists + --ignore-cmdline + Force usage to run target instead of searching for cmdline files + in AFL fuzzing directory + --no-cluster + Do not cluster CASR reports + --casr-gdb-args + Add "--casr-gdb-args './gdb_fuzz_target '" to generate additional + crash reports with casr-gdb (e.g., test whether program crashes without + sanitizers) + -h, --help + Print help + -V, --version + Print version `casr-afl` provides a straightforward CASR integration with AFL++. While walking through afl instances, `casr-afl` generates crash reports depending on target binary. For @@ -556,7 +556,7 @@ you can estimate crash severity for program built without sanitizers. You can set environment variable `RUST_BACKTRACE=(1|full)` for `casr-afl`. This variable may be used by [casr-san](#casr-san). -Example for AFL++-based fuzzer `Sharpfuzz`: +Sharpfuzz example: $ $ cp casr/tests/casr_tests/csharp/test_casr_afl_csharp /tmp/test_casr_afl_csharp $ cp casr/tests/casr_tests/csharp/test_casr_afl_csharp_module /tmp/test_casr_afl_csharp_module From a15354e7c7720d8502766a2e86b0900df8c91e05 Mon Sep 17 00:00:00 2001 From: headshog Date: Mon, 18 Mar 2024 20:29:52 +0300 Subject: [PATCH 21/38] add whet to aarch64 workflow --- .github/workflows/aarch64.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/aarch64.yml b/.github/workflows/aarch64.yml index 92f8f8b1..f2ffcb76 100644 --- a/.github/workflows/aarch64.yml +++ b/.github/workflows/aarch64.yml @@ -25,7 +25,7 @@ jobs: install: | export CARGO_TERM_COLOR=always export CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse - apt-get update && apt-get install -y gdb pip curl python3.10-dev clang llvm build-essential + apt-get update && apt-get install -y gdb pip curl wget python3.10-dev clang llvm build-essential wget https://download.visualstudio.microsoft.com/download/pr/092bec24-9cad-421d-9b43-458b3a7549aa/84280dbd1eef750f9ed1625339235c22/dotnet-sdk-8.0.101-linux-arm64.tar.gz mkdir /dotnet && tar zxf dotnet-sdk-8.0.101-linux-arm64.tar.gz -C /dotnet && rm -rf dotnet-sdk-8.0.101-linux-arm64.tar.gz export DOTNET_ROOT=/dotnet From f570c1bb43d0e083cb5f3aa24a55724de3634d7d Mon Sep 17 00:00:00 2001 From: headshog Date: Tue, 19 Mar 2024 13:05:11 +0300 Subject: [PATCH 22/38] fix afl csharp test --- casr/tests/tests.rs | 11 ++++++++++- docs/usage.md | 5 ++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/casr/tests/tests.rs b/casr/tests/tests.rs index e1e84a4b..b8452570 100644 --- a/casr/tests/tests.rs +++ b/casr/tests/tests.rs @@ -5820,6 +5820,14 @@ fn test_casr_afl_csharp() { 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 @@ -5833,6 +5841,7 @@ fn test_casr_afl_csharp() { "run", "--project", &format!("{}/test_casr_afl_csharp.csproj", &paths[4]), + "--no-build", "@@", ]) .env( @@ -5841,7 +5850,7 @@ fn test_casr_afl_csharp() { ); print!("{:?}", output); - let output = output.output().expect("asfs"); + let output = output.output().expect("casr-afl crashed"); assert!( output.status.success(), diff --git a/docs/usage.md b/docs/usage.md index 60e0459b..28efddb3 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -560,7 +560,10 @@ Sharpfuzz example: $ $ cp casr/tests/casr_tests/csharp/test_casr_afl_csharp /tmp/test_casr_afl_csharp $ cp casr/tests/casr_tests/csharp/test_casr_afl_csharp_module /tmp/test_casr_afl_csharp_module - $ casr-afl -i casr/tests/casr_tests/casrep/afl-out-sharpfuzz -o casr/tests/tmp_tests_casr/casr_afl_csharp_out -- dotnet run --project /tmp/test_casr_afl_csharp/test_casr_afl_csharp.csproj @@ + $ dotnet build /tmp/test_casr_afl_csharp/test_casr_afl_csharp.csproj + $ casr-afl -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 `dotnet`, build your project before run (via `dotnet build` or `dotnet publish`) and specify `--no-build` option for `dotnet run`. ## casr-libfuzzer From 14078af3aa9a8d586857d6a723ab3a8901c6ed7d Mon Sep 17 00:00:00 2001 From: headshog Date: Tue, 19 Mar 2024 16:49:04 +0300 Subject: [PATCH 23/38] refactored casr-afl --- README.md | 15 +-- casr/src/bin/casr-afl.rs | 97 ++++++++----------- .../afl-out-sharpfuzz/afl_main-worker/cmdline | 2 +- .../afl-out-sharpfuzz/afl_s01-worker/cmdline | 2 +- casr/tests/tests.rs | 35 +++---- docs/usage.md | 29 +++--- 6 files changed, 79 insertions(+), 101 deletions(-) diff --git a/README.md b/README.md index d29075c1..a9b78106 100644 --- a/README.md +++ b/README.md @@ -194,17 +194,20 @@ Cluster reports: Triage crashes after AFL++ fuzzing with casr-afl: - $ cp casr/tests/casr_tests/bin/load_afl /tmp/load_afl - $ cp casr/tests/casr_tests/bin/load_sydr /tmp/load_sydr + $ cp -r casr/tests/casr_tests/bin/load_afl /tmp/load_afl + $ cp -r casr/tests/casr_tests/bin/load_sydr /tmp/load_sydr $ casr-afl -i casr/tests/casr_tests/casrep/afl-out-xlnt -o casr/tests/tmp_tests_casr/casr_afl_out $ # You may also additionally generate crash reports for uninstrumented binary with casr-gdb - $ casr-afl -i casr/tests/casr_tests/casrep/afl-out-xlnt -o casr/tests/tmp_tests_casr/casr_afl_out --casr-gdb-args /tmp/load_sydr @@ + $ casr-afl -i casr/tests/casr_tests/casrep/afl-out-xlnt -o casr/tests/tmp_tests_casr/casr_afl_out -- /tmp/load_sydr @@ Triage crashes after Sharpfuzz fuzzing with casr-afl: - $ cp casr/tests/casr_tests/csharp/test_casr_afl_csharp /tmp/test_casr_afl_csharp - $ cp casr/tests/casr_tests/csharp/test_casr_afl_csharp_module /tmp/test_casr_afl_csharp_module - $ casr-afl -i casr/tests/casr_tests/casrep/afl-out-sharpfuzz -o casr/tests/tmp_tests_casr/casr_afl_csharp_out -- dotnet run --project /tmp/test_casr_afl_csharp/test_casr_afl_csharp.csproj @@ + $ 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 -o /tmp/test_casr_afl_csharp/bin + $ 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 @@ Triage libFuzzer crashes with casr-libfuzzer: diff --git a/casr/src/bin/casr-afl.rs b/casr/src/bin/casr-afl.rs index b7b8db96..d7bf9f13 100644 --- a/casr/src/bin/casr-afl.rs +++ b/casr/src/bin/casr-afl.rs @@ -1,8 +1,7 @@ use casr::triage::{fuzzing_crash_triage_pipeline, CrashInfo}; use casr::util; -use anyhow::bail; -use anyhow::Result; +use anyhow::{bail, Result}; use clap::{ error::{ContextKind, ContextValue, ErrorKind}, Arg, ArgAction, @@ -87,7 +86,7 @@ fn main() -> Result<()> { Arg::new("ignore-cmdline") .action(ArgAction::SetTrue) .long("ignore-cmdline") - .help("Force usage to run target instead of searching for cmdline files in AFL fuzzing directory") + .help("Force usage to run target instead of searching for cmdline files in AFL fuzzing directory") ) .arg( Arg::new("no-cluster") @@ -95,53 +94,41 @@ fn main() -> Result<()> { .long("no-cluster") .help("Do not cluster CASR reports") ) - .arg( - Arg::new("casr-gdb-args") - .long("casr-gdb-args") - .action(ArgAction::Set) - .help("Add \"--casr-gdb-args \'./gdb_fuzz_target \'\" to generate additional crash reports with casr-gdb (e.g., test whether program crashes without sanitizers)"), - ) .arg( Arg::new("ARGS") .action(ArgAction::Set) + .required(false) .num_args(1..) .last(true) - .help("Add \"-- fuzz_target \""), + .help("Add \"-- ./gdb_fuzz_target \" to generate additional crash reports with casr-gdb \ + (for compiled binaries, e.g., test whether program crashes without sanitizers), \"-- dotnet \" \ + or \"-- mono \" to triage C# crashes with additional options") ) .get_matches(); // Init log. util::initialize_logging(&matches); - // Get fuzz target args. - let argv: Vec<&str> = if let Some(argvs) = matches.get_many::("ARGS") { - argvs.map(|v| v.as_str()).collect() + let mut args = if let Some(argv) = matches.get_many::("ARGS") { + argv.cloned().collect() } else { Vec::new() }; - // Get gdb args. - let mut gdb_args = if let Some(argv) = matches.get_one::("casr-gdb-args") { - shell_words::split(argv)? - } else { - Vec::new() - }; - if gdb_args.is_empty() && matches.get_flag("ignore-cmdline") { - bail!("casr-gdb-args is empty, but \"ignore-cmdline\" option is provided."); + if args.is_empty() && matches.get_flag("ignore-cmdline") { + bail!("ARGS is empty, but \"ignore-cmdline\" option is provided."); } // Get tool. - let tool = if !argv.is_empty() && (argv[0].ends_with("dotnet") || argv[0].ends_with("mono")) { + let mut tool = if matches.get_flag("ignore-cmdline") + && (args[0].ends_with("dotnet") || args[0].ends_with("mono")) + { "casr-csharp" } else { "casr-gdb" }; let tool_path = util::get_path(tool)?; - if !gdb_args.is_empty() && tool != "casr-gdb" { - bail!("casr-gdb-args is provided with other tool (casr-csharp)."); - } - // Get all crashes. let mut crashes: HashMap = HashMap::new(); for node_dir in fs::read_dir(matches.get_one::("input").unwrap())? { @@ -151,38 +138,30 @@ fn main() -> Result<()> { } // Get crashes from one node. - let mut crash_info = if tool == "casr-gdb" { - CrashInfo { - target_args: if matches.get_flag("ignore-cmdline") { - gdb_args.clone() - } else { - let cmdline_path = path.join("cmdline"); - if let Ok(cmdline) = fs::read_to_string(&cmdline_path) { - cmdline.split_whitespace().map(|s| s.to_string()).collect() - } else { - error!("Couldn't read {}.", cmdline_path.display()); - continue; - } - }, - envs: HashMap::new(), - ..Default::default() - } + let mut crash_info = casr::triage::CrashInfo { + ..Default::default() + }; + if matches.get_flag("ignore-cmdline") { + crash_info.casr_tool = tool_path.clone(); + crash_info.target_args = args.clone() } else { - CrashInfo { - target_args: argv.iter().map(|x| x.to_string()).collect(), - envs: vec![ - ("AFL_SKIP_BIN_CHECK".to_string(), "1".to_string()), - ( - "AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES".to_string(), - "1".to_string(), - ), - ] - .into_iter() - .collect(), - ..Default::default() + let cmdline_path = path.join("cmdline"); + if let Ok(cmdline) = fs::read_to_string(&cmdline_path) { + let cmd_args: Vec = + cmdline.split_whitespace().map(|s| s.to_string()).collect(); + if cmd_args[0].ends_with("dotnet") || cmd_args[0].ends_with("mono") { + tool = "casr-csharp"; + crash_info.casr_tool = util::get_path("casr-csharp")?.clone() + } else { + tool = "casr-gdb"; + crash_info.casr_tool = util::get_path("casr-gdb")?.clone() + } + crash_info.target_args = cmd_args; + } else { + error!("Couldn't read {}.", cmdline_path.display()); + continue; } }; - crash_info.casr_tool = tool_path.clone(); crash_info.at_index = crash_info .target_args .iter() @@ -196,7 +175,7 @@ fn main() -> Result<()> { match util::symbols_list(Path::new(target)) { Ok(list) => { if list.contains("__asan") { - crash_info.casr_tool = util::get_path("casr-san")?.clone(); + crash_info.casr_tool = util::get_path("casr-san")?.clone() } } Err(e) => { @@ -226,10 +205,10 @@ fn main() -> Result<()> { } } - if matches.get_flag("ignore-cmdline") { - gdb_args = Vec::new(); + if matches.get_flag("ignore-cmdline") || tool != "casr-gdb" { + args = Vec::new(); } // Generate reports - fuzzing_crash_triage_pipeline(&matches, &crashes, &gdb_args) + fuzzing_crash_triage_pipeline(&matches, &crashes, &args) } diff --git a/casr/tests/casr_tests/casrep/afl-out-sharpfuzz/afl_main-worker/cmdline b/casr/tests/casr_tests/casrep/afl-out-sharpfuzz/afl_main-worker/cmdline index e5ec54e9..c2b6ed28 100644 --- a/casr/tests/casr_tests/casrep/afl-out-sharpfuzz/afl_main-worker/cmdline +++ b/casr/tests/casr_tests/casrep/afl-out-sharpfuzz/afl_main-worker/cmdline @@ -1,3 +1,3 @@ /usr/bin/dotnet -/build_test/test_casr_afl_csharp/bin/test_casr_afl_csharp.dll +/tmp/test_casr_afl_csharp/bin/test_casr_afl_csharp.dll @@ diff --git a/casr/tests/casr_tests/casrep/afl-out-sharpfuzz/afl_s01-worker/cmdline b/casr/tests/casr_tests/casrep/afl-out-sharpfuzz/afl_s01-worker/cmdline index e5ec54e9..c2b6ed28 100644 --- a/casr/tests/casr_tests/casrep/afl-out-sharpfuzz/afl_s01-worker/cmdline +++ b/casr/tests/casr_tests/casrep/afl-out-sharpfuzz/afl_s01-worker/cmdline @@ -1,3 +1,3 @@ /usr/bin/dotnet -/build_test/test_casr_afl_csharp/bin/test_casr_afl_csharp.dll +/tmp/test_casr_afl_csharp/bin/test_casr_afl_csharp.dll @@ diff --git a/casr/tests/tests.rs b/casr/tests/tests.rs index b8452570..87fbede4 100644 --- a/casr/tests/tests.rs +++ b/casr/tests/tests.rs @@ -3699,8 +3699,9 @@ fn test_casr_afl() { &paths[0], "-o", &paths[1], - "--casr-gdb-args", - "/tmp/load_sydr @@", + "--", + "/tmp/load_sydr", + "@@", ]) .env( "PATH", @@ -3793,7 +3794,7 @@ fn test_casr_afl_ignore_cmd() { &paths[0], "-o", &paths[1], - "--casr-gdb-args", + "--", &load_afl, ]) .output() @@ -5822,32 +5823,20 @@ fn test_casr_afl_csharp() { let _ = Command::new(dotnet_path.to_str().unwrap()) .args([ - "build", + "publish", + "-o", + &format!("{}/bin", &paths[4]), &format!("{}/test_casr_afl_csharp.csproj", &paths[4]), ]) .output() - .expect("dotnet build crashed"); + .expect("dotnet publish 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", - "--project", - &format!("{}/test_casr_afl_csharp.csproj", &paths[4]), - "--no-build", - "@@", - ]) - .env( - "PATH", - format!("{}:{}", bins.display(), std::env::var("PATH").unwrap()), - ); + output.args(["-i", &paths[0], "-o", &paths[1]]).env( + "PATH", + format!("{}:{}", bins.display(), std::env::var("PATH").unwrap()), + ); print!("{:?}", output); let output = output.output().expect("casr-afl crashed"); diff --git a/docs/usage.md b/docs/usage.md index 28efddb3..b59355b3 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -439,7 +439,10 @@ Triage crashes found by AFL++ (Sharpfuzz) Usage: casr-afl [OPTIONS] --input --output [-- ...] Arguments: - [ARGS]... Add "-- fuzz_target " + [ARGS]... Add "-- ./gdb_fuzz_target " to generate additional crash reports + with casr-gdb (for compiled binaries, e.g., test whether program crashes + without sanitizers), "-- dotnet " or "-- mono " to + triage C# crashes with additional options Options: -l, --log-level @@ -460,10 +463,6 @@ Triage crashes found by AFL++ (Sharpfuzz) in AFL fuzzing directory --no-cluster Do not cluster CASR reports - --casr-gdb-args - Add "--casr-gdb-args './gdb_fuzz_target '" to generate additional - crash reports with casr-gdb (e.g., test whether program crashes without - sanitizers) -h, --help Print help -V, --version @@ -483,8 +482,8 @@ fuzzer [Sharpfuzz](https://github.com/Metalnem/sharpfuzz). AFL++ Example (Ubuntu 20.04+): - $ cp casr/tests/casr_tests/bin/load_afl /tmp/load_afl - $ cp casr/tests/casr_tests/bin/load_sydr /tmp/load_sydr + $ cp -r casr/tests/casr_tests/bin/load_afl /tmp/load_afl + $ cp -r casr/tests/casr_tests/bin/load_sydr /tmp/load_sydr $ casr-afl -i casr/tests/casr_tests/casrep/afl-out-xlnt -o casr/tests/tmp_tests_casr/casr_afl_out $ tree tests/tmp_tests_casr/casr_afl_out @@ -558,12 +557,20 @@ variable may be used by [casr-san](#casr-san). Sharpfuzz example: - $ $ cp casr/tests/casr_tests/csharp/test_casr_afl_csharp /tmp/test_casr_afl_csharp - $ cp casr/tests/casr_tests/csharp/test_casr_afl_csharp_module /tmp/test_casr_afl_csharp_module + $ 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 -o /tmp/test_casr_afl_csharp/bin + $ casr-afl -i casr/tests/casr_tests/casrep/afl-out-sharpfuzz -o casr/tests/tmp_tests_casr/casr_afl_csharp_out + +Sharpfuzz example (with --ignore-cmdline): + + $ 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 build /tmp/test_casr_afl_csharp/test_casr_afl_csharp.csproj - $ casr-afl -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 @@ + $ 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 `dotnet`, build your project before run (via `dotnet build` or `dotnet publish`) and specify `--no-build` option for `dotnet run`. +**NOTE:** 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`. ## casr-libfuzzer From 0ac0078034db01b103b5bf0583efa49e66ff9a2b Mon Sep 17 00:00:00 2001 From: headshog Date: Tue, 19 Mar 2024 18:11:25 +0300 Subject: [PATCH 24/38] add casr-afl_csharp ignore_cmdline test --- casr/tests/tests.rs | 126 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) diff --git a/casr/tests/tests.rs b/casr/tests/tests.rs index 87fbede4..a36f1358 100644 --- a/casr/tests/tests.rs +++ b/casr/tests/tests.rs @@ -5905,3 +5905,129 @@ fn test_casr_afl_csharp() { .output() .expect("failed to remove dir"); } + +#[test] +#[cfg(target_arch = "x86_64")] +fn test_casr_afl_csharp_ignore_cmd() { + use std::collections::HashMap; + + let paths = [ + abs_path("tests/casr_tests/casrep/afl-out-sharpfuzz"), + abs_path("tests/tmp_tests_casr/casr_afl_csharp_ignore_cmd_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::create_dir(abs_path("tests/tmp_tests_casr")); + let _ = Command::new("cp") + .args(["-r", &paths[2], &paths[4]]) + .output() + .expect("failed to copy dir"); + let _ = Command::new("cp") + .args(["-r", &paths[3], &paths[5]]) + .output() + .expect("failed to copy dir"); + 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 publish crashed"); + + let bins = Path::new(*EXE_CASR_AFL.read().unwrap()).parent().unwrap(); + let mut output = Command::new(*EXE_CASR_AFL.read().unwrap()); + output + .args([ + "--ignore-cmdline", + "-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()), + ); + + print!("{:?}", output); + 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\d+)").unwrap(); + let unique_cnt = re + .captures(&res) + .unwrap() + .name("unique") + .map(|x| x.as_str()) + .unwrap() + .parse::() + .unwrap(); + + assert_eq!(unique_cnt, 3, "Invalid number of deduplicated reports"); + + let re = Regex::new(r"Number of clusters: (?P\d+)").unwrap(); + let clusters_cnt = re + .captures(&res) + .unwrap() + .name("clusters") + .map(|x| x.as_str()) + .unwrap() + .parse::() + .unwrap(); + + assert_eq!(clusters_cnt, 3, "Invalid number of clusters"); + + let mut storage: HashMap = 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 _ = Command::new("rm") + .args(["-rf", &paths[4]]) + .output() + .expect("failed to remove dir"); + let _ = Command::new("rm") + .args(["-rf", &paths[5]]) + .output() + .expect("failed to remove dir"); +} From ebdc91d34ecd850e78e5d3ac90da638124ef7a00 Mon Sep 17 00:00:00 2001 From: headshog Date: Tue, 19 Mar 2024 19:43:26 +0300 Subject: [PATCH 25/38] Fix usage --- casr/src/bin/casr-libfuzzer.rs | 2 +- docs/usage.md | 49 ++++++++++++++-------------------- 2 files changed, 21 insertions(+), 30 deletions(-) diff --git a/casr/src/bin/casr-libfuzzer.rs b/casr/src/bin/casr-libfuzzer.rs index ae5d56fe..c215b1ff 100644 --- a/casr/src/bin/casr-libfuzzer.rs +++ b/casr/src/bin/casr-libfuzzer.rs @@ -113,7 +113,7 @@ fn main() -> Result<()> { let mut argv: Vec<&str> = if let Some(argvs) = matches.get_many::("ARGS") { argvs.map(|v| v.as_str()).collect() } else { - Vec::new() + bail!("Invalid fuzz target arguments"); }; // Get gdb args. diff --git a/docs/usage.md b/docs/usage.md index b59355b3..ee08cbc3 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -214,13 +214,13 @@ Create CASR reports (.casrep) from C# reports Options: -o, --output Path to save report. Path can be a directory, then report name - is generated + is generated --stdout Print CASR report to stdout --stdin Stdin file for program -t, --timeout Timeout (in seconds) for target execution, 0 value means that - timeout is disabled [default: 0] + timeout is disabled [default: 0] --ignore File with regular expressions for functions and file paths that - should be ignored + should be ignored -h, --help Print help -V, --version Print version @@ -440,33 +440,24 @@ Triage crashes found by AFL++ (Sharpfuzz) Arguments: [ARGS]... Add "-- ./gdb_fuzz_target " to generate additional crash reports - with casr-gdb (for compiled binaries, e.g., test whether program crashes - without sanitizers), "-- dotnet " or "-- mono " to - triage C# crashes with additional options + with casr-gdb (for compiled binaries, e.g., test whether program crashes + without sanitizers), "-- dotnet " or "-- mono " to + triage C# crashes with additional options Options: - -l, --log-level - Logging level [default: info] [possible values: info, debug] - -j, --jobs - Number of parallel jobs for generating CASR reports [default: half of cpu cores] - -t, --timeout - Timeout (in seconds) for target execution, 0 value means that timeout is - disabled [default: 0] - -i, --input - AFL++ work directory - -o, --output - Output directory with triaged reports - -f, --force-remove - Remove output project directory if it exists - --ignore-cmdline - Force usage to run target instead of searching for cmdline files - in AFL fuzzing directory - --no-cluster - Do not cluster CASR reports - -h, --help - Print help - -V, --version - Print version + -l, --log-level Logging level [default: info] [possible values: info, debug] + -j, --jobs Number of parallel jobs for generating CASR reports + [default: half of cpu cores] + -t, --timeout Timeout (in seconds) for target execution, 0 value means + that timeout is disabled [default: 0] + -i, --input AFL++ work directory + -o, --output Output directory with triaged reports + -f, --force-remove Remove output project directory if it exists + --ignore-cmdline Force usage to run target instead of searching for cmdline files + in AFL fuzzing directory + --no-cluster Do not cluster CASR reports + -h, --help Print help + -V, --version Print version `casr-afl` provides a straightforward CASR integration with AFL++. While walking through afl instances, `casr-afl` generates crash reports depending on target binary. For @@ -547,7 +538,7 @@ AFL++ Example (Ubuntu 20.04+): You may also run `casr-afl` with additional report generation for uninstrumented binary with `casr-gdb`: - $ casr-afl -i casr/tests/casr_tests/casrep/afl-out-xlnt -o casr/tests/tmp_tests_casr/casr_afl_out --casr-gdb-args /tmp/load_sydr @@ + $ casr-afl -i casr/tests/casr_tests/casrep/afl-out-xlnt -o casr/tests/tmp_tests_casr/casr_afl_out -- /tmp/load_sydr @@ Thus, `casr-afl` will generate GDB crash report for each unique ASAN crash. So, you can estimate crash severity for program built without sanitizers. From 9a22947e2951c86af4de26d879cc3dd79c17bad5 Mon Sep 17 00:00:00 2001 From: headshog Date: Wed, 20 Mar 2024 21:52:09 +0300 Subject: [PATCH 26/38] Fix by review --- .github/workflows/aarch64.yml | 8 +------- README.md | 3 ++- casr/src/bin/casr-afl.rs | 8 ++++---- casr/src/bin/casr-csharp.rs | 24 ++++++++++++------------ casr/tests/tests.rs | 21 ++++----------------- docs/usage.md | 10 ++++++---- 6 files changed, 29 insertions(+), 45 deletions(-) diff --git a/.github/workflows/aarch64.yml b/.github/workflows/aarch64.yml index f2ffcb76..4817d35e 100644 --- a/.github/workflows/aarch64.yml +++ b/.github/workflows/aarch64.yml @@ -25,13 +25,7 @@ jobs: install: | export CARGO_TERM_COLOR=always export CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse - apt-get update && apt-get install -y gdb pip curl wget python3.10-dev clang llvm build-essential - wget https://download.visualstudio.microsoft.com/download/pr/092bec24-9cad-421d-9b43-458b3a7549aa/84280dbd1eef750f9ed1625339235c22/dotnet-sdk-8.0.101-linux-arm64.tar.gz - mkdir /dotnet && tar zxf dotnet-sdk-8.0.101-linux-arm64.tar.gz -C /dotnet && rm -rf dotnet-sdk-8.0.101-linux-arm64.tar.gz - export DOTNET_ROOT=/dotnet - export PATH=$PATH:/dotnet - export DOTNET_CLI_TELEMETRY_OPTOUT=1 - export DOTNET_UPGRADEASSISTANT_TELEMETRY_OPTOUT=1 + apt-get update && apt-get install -y gdb pip curl python3.10-dev clang llvm build-essential curl https://sh.rustup.rs -o rustup.sh && chmod +x rustup.sh && \ ./rustup.sh -y && rm rustup.sh run: | diff --git a/README.md b/README.md index a9b78106..46332206 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,8 @@ java reports and get report from to analyze JavaScript reports and get report from [Jazzer.js](https://github.com/CodeIntelligenceTesting/jazzer.js) or [jsfuzz](https://github.com/fuzzitdev/jsfuzz). -Use `casr-csharp` to analyze C# reports. +Use `casr-csharp` to analyze C# reports and get report from +[Sharpfuzz](https://github.com/Metalnem/sharpfuzz). Crash report contains many useful information: severity (like [exploitable](https://github.com/jfoote/exploitable)) for x86, x86\_64, arm32, aarch64, rv32g, rv64g architectures, diff --git a/casr/src/bin/casr-afl.rs b/casr/src/bin/casr-afl.rs index d7bf9f13..36fd80f2 100644 --- a/casr/src/bin/casr-afl.rs +++ b/casr/src/bin/casr-afl.rs @@ -15,7 +15,7 @@ use std::path::{Path, PathBuf}; fn main() -> Result<()> { let matches = clap::Command::new("casr-afl") .version(clap::crate_version!()) - .about("Triage crashes found by AFL++ (Sharpfuzz)") + .about("Triage crashes found by AFL++/Sharpfuzz") .term_width(90) .arg( Arg::new("log-level") @@ -151,10 +151,10 @@ fn main() -> Result<()> { cmdline.split_whitespace().map(|s| s.to_string()).collect(); if cmd_args[0].ends_with("dotnet") || cmd_args[0].ends_with("mono") { tool = "casr-csharp"; - crash_info.casr_tool = util::get_path("casr-csharp")?.clone() + crash_info.casr_tool = util::get_path("casr-csharp")?; } else { tool = "casr-gdb"; - crash_info.casr_tool = util::get_path("casr-gdb")?.clone() + crash_info.casr_tool = util::get_path("casr-gdb")?; } crash_info.target_args = cmd_args; } else { @@ -209,6 +209,6 @@ fn main() -> Result<()> { args = Vec::new(); } - // Generate reports + // Generate reports. fuzzing_crash_triage_pipeline(&matches, &crashes, &args) } diff --git a/casr/src/bin/casr-csharp.rs b/casr/src/bin/casr-csharp.rs index 37f0fdbe..11250caf 100644 --- a/casr/src/bin/casr-csharp.rs +++ b/casr/src/bin/casr-csharp.rs @@ -83,10 +83,18 @@ fn main() -> Result<()> { bail!("Wrong arguments for starting program"); }; + // Check that args are valid. + let Some(pos) = argv + .iter() + .position(|x| x.ends_with(".dll") || x.ends_with(".exe") || x.ends_with(".csproj")) + else { + bail!("dotnet/mono target is not specified by .dll, .exe or .csproj executable."); + }; + // Get stdin for target program. let stdin_file = util::stdin_from_matches(&matches)?; - // Get timeout + // Get timeout. let timeout = *matches.get_one::("timeout").unwrap(); // Run program. @@ -103,16 +111,8 @@ fn main() -> Result<()> { // Create report. let mut report = CrashReport::new(); - // Set executable path (for C# .dll (dotnet) or .exe (mono) file) - if let Some(pos) = argv - .iter() - .position(|x| x.ends_with(".dll") || x.ends_with(".exe") || x.ends_with(".csproj")) - { - let Some(classes) = argv.get(pos) else { - bail!("dotnet target is not specified by .dll, .exe or .csproj executable."); - }; - report.executable_path = classes.to_string(); - } + // Set executable path (for C# .dll, .csproj (dotnet) or .exe (mono) file). + report.executable_path = argv.get(pos).unwrap().to_string(); report.proc_cmdline = argv.join(" "); let _ = report.add_os_info(); let _ = report.add_proc_environ(); @@ -139,6 +139,6 @@ fn main() -> Result<()> { } } - //Output report + //Output report. util::output_report(&report, &matches, &argv) } diff --git a/casr/tests/tests.rs b/casr/tests/tests.rs index a36f1358..990c2f98 100644 --- a/casr/tests/tests.rs +++ b/casr/tests/tests.rs @@ -5838,7 +5838,6 @@ fn test_casr_afl_csharp() { format!("{}:{}", bins.display(), std::env::var("PATH").unwrap()), ); - print!("{:?}", output); let output = output.output().expect("casr-afl crashed"); assert!( @@ -5896,14 +5895,8 @@ fn test_casr_afl_csharp() { } assert!(storage.values().all(|x| *x > 1)); - let _ = Command::new("rm") - .args(["-rf", &paths[4]]) - .output() - .expect("failed to remove dir"); - let _ = Command::new("rm") - .args(["-rf", &paths[5]]) - .output() - .expect("failed to remove dir"); + let _ = fs::remove_dir_all(&paths[4]); + let _ = fs::remove_dir_all(&paths[5]); } #[test] @@ -6022,12 +6015,6 @@ fn test_casr_afl_csharp_ignore_cmd() { } assert!(storage.values().all(|x| *x > 1)); - let _ = Command::new("rm") - .args(["-rf", &paths[4]]) - .output() - .expect("failed to remove dir"); - let _ = Command::new("rm") - .args(["-rf", &paths[5]]) - .output() - .expect("failed to remove dir"); + let _ = fs::remove_dir_all(&paths[4]); + let _ = fs::remove_dir_all(&paths[5]); } diff --git a/docs/usage.md b/docs/usage.md index ee08cbc3..3fece128 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -10,8 +10,10 @@ java reports and get report from to analyze JavaScript reports and get report from [Jazzer.js](https://github.com/CodeIntelligenceTesting/jazzer.js) or [jsfuzz](https://github.com/fuzzitdev/jsfuzz). -Use `casr-csharp` to analyze C# reports. `casr-afl` can triage crashes -found by [AFL++](https://github.com/AFLplusplus/AFLplusplus) (Sharpfuzz). +Use `casr-csharp` to analyze C# reports and get report from +[Sharpfuzz](https://github.com/Metalnem/sharpfuzz). `casr-afl` can triage +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 @@ -473,8 +475,8 @@ fuzzer [Sharpfuzz](https://github.com/Metalnem/sharpfuzz). AFL++ Example (Ubuntu 20.04+): - $ cp -r casr/tests/casr_tests/bin/load_afl /tmp/load_afl - $ cp -r casr/tests/casr_tests/bin/load_sydr /tmp/load_sydr + $ cp casr/tests/casr_tests/bin/load_afl /tmp/load_afl + $ cp casr/tests/casr_tests/bin/load_sydr /tmp/load_sydr $ casr-afl -i casr/tests/casr_tests/casrep/afl-out-xlnt -o casr/tests/tmp_tests_casr/casr_afl_out $ tree tests/tmp_tests_casr/casr_afl_out From d28447b2a15017f82cd9a108d3e6bb5061197ec2 Mon Sep 17 00:00:00 2001 From: headshog Date: Thu, 21 Mar 2024 11:15:33 +0300 Subject: [PATCH 27/38] Fixes --- README.md | 4 ++-- casr/Cargo.toml | 1 + casr/tests/tests.rs | 22 ++++++---------------- libcasr/src/lib.rs | 1 + 4 files changed, 10 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 46332206..8e9efb55 100644 --- a/README.md +++ b/README.md @@ -195,8 +195,8 @@ Cluster reports: Triage crashes after AFL++ fuzzing with casr-afl: - $ cp -r casr/tests/casr_tests/bin/load_afl /tmp/load_afl - $ cp -r casr/tests/casr_tests/bin/load_sydr /tmp/load_sydr + $ cp casr/tests/casr_tests/bin/load_afl /tmp/load_afl + $ cp casr/tests/casr_tests/bin/load_sydr /tmp/load_sydr $ casr-afl -i casr/tests/casr_tests/casrep/afl-out-xlnt -o casr/tests/tmp_tests_casr/casr_afl_out $ # You may also additionally generate crash reports for uninstrumented binary with casr-gdb $ casr-afl -i casr/tests/casr_tests/casrep/afl-out-xlnt -o casr/tests/tmp_tests_casr/casr_afl_out -- /tmp/load_sydr @@ diff --git a/casr/Cargo.toml b/casr/Cargo.toml index ab09d2bf..f6cb4e05 100644 --- a/casr/Cargo.toml +++ b/casr/Cargo.toml @@ -37,6 +37,7 @@ tokio = { version = "1", features = ["rt", "macros"], optional = true } toml = { version = "0.7", optional = true } wait-timeout = "0.2" which = "4.4" +copy_dir = "0.1.3" libcasr = { path = "../libcasr", version = "2.11.1", features = ["serde", "exploitable"] } diff --git a/casr/tests/tests.rs b/casr/tests/tests.rs index 990c2f98..52640e8c 100644 --- a/casr/tests/tests.rs +++ b/casr/tests/tests.rs @@ -1,11 +1,13 @@ extern crate lazy_static; extern crate regex; extern crate serde_json; +extern crate copy_dir; use regex::Regex; use serde_json::Value; use std::env; use std::fs; +use copy_dir::copy_dir; use std::io::Write; use std::path::{Path, PathBuf}; @@ -5809,14 +5811,8 @@ fn test_casr_afl_csharp() { let _ = fs::remove_dir_all(&paths[1]); let _ = fs::create_dir(abs_path("tests/tmp_tests_casr")); - let _ = Command::new("cp") - .args(["-r", &paths[2], &paths[4]]) - .output() - .expect("failed to copy dir"); - let _ = Command::new("cp") - .args(["-r", &paths[3], &paths[5]]) - .output() - .expect("failed to copy dir"); + let _ = copy_dir(&paths[2], &paths[4]); + let _ = copy_dir(&paths[3], &paths[5]); let Ok(dotnet_path) = which::which("dotnet") else { panic!("No dotnet is found."); }; @@ -5915,14 +5911,8 @@ fn test_casr_afl_csharp_ignore_cmd() { let _ = fs::remove_dir_all(&paths[1]); let _ = fs::create_dir(abs_path("tests/tmp_tests_casr")); - let _ = Command::new("cp") - .args(["-r", &paths[2], &paths[4]]) - .output() - .expect("failed to copy dir"); - let _ = Command::new("cp") - .args(["-r", &paths[3], &paths[5]]) - .output() - .expect("failed to copy dir"); + let _ = copy_dir(&paths[2], &paths[4]); + let _ = copy_dir(&paths[3], &paths[5]); let Ok(dotnet_path) = which::which("dotnet") else { panic!("No dotnet is found."); }; diff --git a/libcasr/src/lib.rs b/libcasr/src/lib.rs index 08476134..0dc1d90f 100644 --- a/libcasr/src/lib.rs +++ b/libcasr/src/lib.rs @@ -17,6 +17,7 @@ //! * Python //! * Java //! * JavaScript +//! * C# //! //! It could be built with `exploitable` feature for severity estimation crashes //! collected from gdb. To save crash reports as json (.casrep/.sarif) use `serde` feature. From 9c869cfd0eca099bb6c09ee8391b3c5e4f9c01a6 Mon Sep 17 00:00:00 2001 From: headshog Date: Thu, 21 Mar 2024 20:10:46 +0300 Subject: [PATCH 28/38] Fixes again --- casr/Cargo.toml | 3 +- casr/tests/tests.rs | 98 ++++++--------------------------------------- docs/usage.md | 4 +- 3 files changed, 17 insertions(+), 88 deletions(-) diff --git a/casr/Cargo.toml b/casr/Cargo.toml index f6cb4e05..1e350c1f 100644 --- a/casr/Cargo.toml +++ b/casr/Cargo.toml @@ -37,7 +37,6 @@ tokio = { version = "1", features = ["rt", "macros"], optional = true } toml = { version = "0.7", optional = true } wait-timeout = "0.2" which = "4.4" -copy_dir = "0.1.3" libcasr = { path = "../libcasr", version = "2.11.1", features = ["serde", "exploitable"] } @@ -53,3 +52,5 @@ required-features = ["dojo"] [dev-dependencies] lazy_static = "1.4" +lsb_release = "0.1" +copy_dir = "0.1.3" diff --git a/casr/tests/tests.rs b/casr/tests/tests.rs index 52640e8c..e910d309 100644 --- a/casr/tests/tests.rs +++ b/casr/tests/tests.rs @@ -1,13 +1,13 @@ +extern crate copy_dir; extern crate lazy_static; extern crate regex; extern crate serde_json; -extern crate copy_dir; +use copy_dir::copy_dir; use regex::Regex; use serde_json::Value; use std::env; use std::fs; -use copy_dir::copy_dir; use std::io::Write; use std::path::{Path, PathBuf}; @@ -3867,18 +3867,7 @@ fn test_casr_ubsan() { let test_dir = abs_path("tests/tmp_tests_casr/test_casr_ubsan"); let _ = fs::remove_dir_all(&test_dir); - - let output = Command::new("cp") - .args(["-r", &work_dir, &test_dir]) - .output() - .expect("failed to copy dir"); - - assert!( - output.status.success(), - "Stdout {}.\n Stderr: {}", - String::from_utf8_lossy(&output.stdout), - String::from_utf8_lossy(&output.stderr) - ); + let _ = copy_dir(work_dir, &test_dir); let paths = [ abs_path("tests/tmp_tests_casr/test_casr_ubsan/test_ubsan.cpp"), @@ -4378,17 +4367,7 @@ fn test_casr_san_python_df() { let work_dir = abs_path("tests/casr_tests/python"); let test_dir = abs_path("tests/tmp_tests_casr/test_casr_san_python_df"); - let output = Command::new("cp") - .args(["-r", &work_dir, &test_dir]) - .output() - .expect("failed to copy dir"); - - assert!( - output.status.success(), - "Stdout {}.\n Stderr: {}", - String::from_utf8_lossy(&output.stdout), - String::from_utf8_lossy(&output.stderr) - ); + let _ = copy_dir(work_dir, &test_dir); let paths = [ abs_path("tests/tmp_tests_casr/test_casr_san_python_df/cpp_module.cpp"), @@ -4485,17 +4464,7 @@ fn test_casr_san_atheris_df() { let work_dir = abs_path("tests/casr_tests/python"); let test_dir = abs_path("tests/tmp_tests_casr/test_casr_san_atheris_df"); - let output = Command::new("cp") - .args(["-r", &work_dir, &test_dir]) - .output() - .expect("failed to copy dir"); - - assert!( - output.status.success(), - "Stdout {}.\n Stderr: {}", - String::from_utf8_lossy(&output.stdout), - String::from_utf8_lossy(&output.stderr) - ); + let _ = copy_dir(work_dir, &test_dir); let paths = [ abs_path("tests/tmp_tests_casr/test_casr_san_atheris_df/cpp_module.cpp"), @@ -4596,17 +4565,7 @@ fn test_casr_python_call_san_df() { let work_dir = abs_path("tests/casr_tests/python"); let test_dir = abs_path("tests/tmp_tests_casr/test_casr_python_call_san_df"); - let output = Command::new("cp") - .args(["-r", &work_dir, &test_dir]) - .output() - .expect("failed to copy dir"); - - assert!( - output.status.success(), - "Stdout {}.\n Stderr: {}", - String::from_utf8_lossy(&output.stdout), - String::from_utf8_lossy(&output.stderr) - ); + let _ = copy_dir(work_dir, &test_dir); let paths = [ abs_path("tests/tmp_tests_casr/test_casr_python_call_san_df/cpp_module.cpp"), @@ -4964,19 +4923,9 @@ fn test_casr_js_native() { // Copy files to tmp dir let work_dir = abs_path("tests/casr_tests/js"); let test_dir = abs_path("tests/tmp_tests_casr/test_casr_js_native"); - let _ = std::fs::remove_dir_all(&test_dir); - let output = Command::new("cp") - .args(["-r", &work_dir, &test_dir]) - .output() - .expect("failed to copy dir"); - - assert!( - output.status.success(), - "Stdout {}.\n Stderr: {}", - String::from_utf8_lossy(&output.stdout), - String::from_utf8_lossy(&output.stderr) - ); + let _ = std::fs::remove_dir_all(&test_dir); + let _ = copy_dir(work_dir, &test_dir); let paths = [ abs_path("tests"), @@ -5099,19 +5048,9 @@ fn test_casr_js_native_jsfuzz() { // Copy files to tmp dir let work_dir = abs_path("tests/casr_tests/js"); let test_dir = abs_path("tests/tmp_tests_casr/test_casr_js_native_jsfuzz"); - let _ = std::fs::remove_dir_all(&test_dir); - - let output = Command::new("cp") - .args(["-r", &work_dir, &test_dir]) - .output() - .expect("failed to copy dir"); - assert!( - output.status.success(), - "Stdout {}.\n Stderr: {}", - String::from_utf8_lossy(&output.stdout), - String::from_utf8_lossy(&output.stderr) - ); + let _ = std::fs::remove_dir_all(&test_dir); + let _ = copy_dir(work_dir, &test_dir); let paths = [ abs_path("tests"), @@ -5234,19 +5173,9 @@ fn test_casr_js_native_jazzer() { // Copy files to tmp dir let work_dir = abs_path("tests/casr_tests/js"); let test_dir = abs_path("tests/tmp_tests_casr/test_casr_js_native_jazzer"); - let _ = std::fs::remove_dir_all(&test_dir); - - let output = Command::new("cp") - .args(["-r", &work_dir, &test_dir]) - .output() - .expect("failed to copy dir"); - assert!( - output.status.success(), - "Stdout {}.\n Stderr: {}", - String::from_utf8_lossy(&output.stdout), - String::from_utf8_lossy(&output.stderr) - ); + let _ = std::fs::remove_dir_all(&test_dir); + let _ = copy_dir(work_dir, &test_dir); let paths = [ abs_path("tests"), @@ -5923,7 +5852,7 @@ fn test_casr_afl_csharp_ignore_cmd() { &format!("{}/test_casr_afl_csharp.csproj", &paths[4]), ]) .output() - .expect("dotnet publish crashed"); + .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()); @@ -5947,7 +5876,6 @@ fn test_casr_afl_csharp_ignore_cmd() { format!("{}:{}", bins.display(), std::env::var("PATH").unwrap()), ); - print!("{:?}", output); let output = output.output().expect("casr-afl crashed"); assert!( diff --git a/docs/usage.md b/docs/usage.md index 3fece128..082b580b 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -436,7 +436,7 @@ Convert reports to SARIF report: ## casr-afl -Triage crashes found by AFL++ (Sharpfuzz) +Triage crashes found by AFL++/Sharpfuzz Usage: casr-afl [OPTIONS] --input --output [-- ...] @@ -455,7 +455,7 @@ Triage crashes found by AFL++ (Sharpfuzz) -i, --input AFL++ work directory -o, --output Output directory with triaged reports -f, --force-remove Remove output project directory if it exists - --ignore-cmdline Force usage to run target instead of searching for cmdline files + --ignore-cmdline Force usage to run target instead of searching for cmdline files in AFL fuzzing directory --no-cluster Do not cluster CASR reports -h, --help Print help From 8b2d680c593d37e6216d5699a20b60b5bd414e64 Mon Sep 17 00:00:00 2001 From: headshog Date: Tue, 26 Mar 2024 12:15:25 +0300 Subject: [PATCH 29/38] Rebase --- libcasr/src/report.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libcasr/src/report.rs b/libcasr/src/report.rs index 698f60e2..52ae6e03 100644 --- a/libcasr/src/report.rs +++ b/libcasr/src/report.rs @@ -227,7 +227,13 @@ pub struct CrashReport { )] #[cfg_attr(feature = "serde", serde(default))] pub js_report: Vec, + /// C# report. + #[cfg_attr( + feature = "serde", serde(rename(serialize = "CSharpReport", deserialize = "CSharpReport")) + )] + #[cfg_attr(feature = "serde", serde(default))] + pub csharp_report: Vec, /// Crash line from stack trace: source:line or binary+offset. #[cfg_attr( feature = "serde", From 615f0e42d5f9a9fe4cce2695faaad9c2c1d30336 Mon Sep 17 00:00:00 2001 From: headshog Date: Tue, 26 Mar 2024 12:18:10 +0300 Subject: [PATCH 30/38] Fix cargo.toml --- casr/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/casr/Cargo.toml b/casr/Cargo.toml index 1e350c1f..204323fe 100644 --- a/casr/Cargo.toml +++ b/casr/Cargo.toml @@ -52,5 +52,4 @@ required-features = ["dojo"] [dev-dependencies] lazy_static = "1.4" -lsb_release = "0.1" copy_dir = "0.1.3" From 494463cc93808ca01dbf41f287f769706cd1dfd9 Mon Sep 17 00:00:00 2001 From: headshog Date: Wed, 27 Mar 2024 18:30:32 +0300 Subject: [PATCH 31/38] Handle copy_dir and remove range from .value_parser --- casr/src/bin/casr-afl.rs | 2 +- casr/src/bin/casr-cli.rs | 4 ---- casr/src/bin/casr-csharp.rs | 2 +- casr/src/bin/casr-gdb.rs | 2 +- casr/src/bin/casr-java.rs | 2 +- casr/src/bin/casr-js.rs | 2 +- casr/src/bin/casr-libfuzzer.rs | 2 +- casr/src/bin/casr-python.rs | 2 +- casr/src/bin/casr-san.rs | 2 +- casr/src/bin/casr-ubsan.rs | 2 +- casr/tests/tests.rs | 22 +++++++++++----------- 11 files changed, 20 insertions(+), 24 deletions(-) diff --git a/casr/src/bin/casr-afl.rs b/casr/src/bin/casr-afl.rs index 36fd80f2..2aced504 100644 --- a/casr/src/bin/casr-afl.rs +++ b/casr/src/bin/casr-afl.rs @@ -40,7 +40,7 @@ fn main() -> Result<()> { .default_value("0") .value_name("SECONDS") .help("Timeout (in seconds) for target execution, 0 value means that timeout is disabled") - .value_parser(clap::value_parser!(u64).range(0..)) + .value_parser(clap::value_parser!(u64)) ) .arg( Arg::new("input") diff --git a/casr/src/bin/casr-cli.rs b/casr/src/bin/casr-cli.rs index 9a2bbd4d..4229804f 100644 --- a/casr/src/bin/casr-cli.rs +++ b/casr/src/bin/casr-cli.rs @@ -669,10 +669,6 @@ fn build_slider_report( select.add_item("CSharpReport", report.csharp_report.join("\n")); } - if !report.csharp_report.is_empty() { - select.add_item("CSharpReport", report.csharp_report.join("\n")); - } - if !report.source.is_empty() { select.add_item("Source", report.source.join("\n")); } diff --git a/casr/src/bin/casr-csharp.rs b/casr/src/bin/casr-csharp.rs index 11250caf..d24a6d48 100644 --- a/casr/src/bin/casr-csharp.rs +++ b/casr/src/bin/casr-csharp.rs @@ -52,7 +52,7 @@ fn main() -> Result<()> { .default_value("0") .value_name("SECONDS") .help("Timeout (in seconds) for target execution, 0 value means that timeout is disabled") - .value_parser(clap::value_parser!(u64).range(0..)) + .value_parser(clap::value_parser!(u64)) ) .arg( Arg::new("ignore") diff --git a/casr/src/bin/casr-gdb.rs b/casr/src/bin/casr-gdb.rs index 51fddf8e..5436ddf8 100644 --- a/casr/src/bin/casr-gdb.rs +++ b/casr/src/bin/casr-gdb.rs @@ -69,7 +69,7 @@ fn main() -> Result<()> { .default_value("0") .value_name("SECONDS") .help("Timeout (in seconds) for target execution, 0 value means that timeout is disabled") - .value_parser(clap::value_parser!(u64).range(0..)) + .value_parser(clap::value_parser!(u64)) ) .arg( Arg::new("ignore") diff --git a/casr/src/bin/casr-java.rs b/casr/src/bin/casr-java.rs index 71e4fe66..917119ea 100644 --- a/casr/src/bin/casr-java.rs +++ b/casr/src/bin/casr-java.rs @@ -64,7 +64,7 @@ fn main() -> Result<()> { .default_value("0") .value_name("SECONDS") .help("Timeout (in seconds) for target execution, 0 value means that timeout is disabled") - .value_parser(clap::value_parser!(u64).range(0..)) + .value_parser(clap::value_parser!(u64)) ) .arg( Arg::new("ignore") diff --git a/casr/src/bin/casr-js.rs b/casr/src/bin/casr-js.rs index 96acc8c7..1762ea1b 100644 --- a/casr/src/bin/casr-js.rs +++ b/casr/src/bin/casr-js.rs @@ -52,7 +52,7 @@ fn main() -> Result<()> { .default_value("0") .value_name("SECONDS") .help("Timeout (in seconds) for target execution, 0 value means that timeout is disabled") - .value_parser(clap::value_parser!(u64).range(0..)) + .value_parser(clap::value_parser!(u64)) ) .arg( Arg::new("ignore") diff --git a/casr/src/bin/casr-libfuzzer.rs b/casr/src/bin/casr-libfuzzer.rs index c215b1ff..9c1e83fc 100644 --- a/casr/src/bin/casr-libfuzzer.rs +++ b/casr/src/bin/casr-libfuzzer.rs @@ -39,7 +39,7 @@ fn main() -> Result<()> { .default_value("0") .value_name("SECONDS") .help("Timeout (in seconds) for target execution, 0 means that timeout is disabled") - .value_parser(clap::value_parser!(u64).range(0..)) + .value_parser(clap::value_parser!(u64)) ) .arg( Arg::new("input") diff --git a/casr/src/bin/casr-python.rs b/casr/src/bin/casr-python.rs index 70b3e0ef..bdfa0b63 100644 --- a/casr/src/bin/casr-python.rs +++ b/casr/src/bin/casr-python.rs @@ -56,7 +56,7 @@ fn main() -> Result<()> { .default_value("0") .value_name("SECONDS") .help("Timeout (in seconds) for target execution, 0 value means that timeout is disabled") - .value_parser(clap::value_parser!(u64).range(0..)) + .value_parser(clap::value_parser!(u64)) ) .arg( Arg::new("ignore") diff --git a/casr/src/bin/casr-san.rs b/casr/src/bin/casr-san.rs index bd85cf44..29be269e 100644 --- a/casr/src/bin/casr-san.rs +++ b/casr/src/bin/casr-san.rs @@ -72,7 +72,7 @@ fn main() -> Result<()> { .default_value("0") .value_name("SECONDS") .help("Timeout (in seconds) for target execution, 0 value means that timeout is disabled") - .value_parser(clap::value_parser!(u64).range(0..)) + .value_parser(clap::value_parser!(u64)) ) .arg( Arg::new("ignore") diff --git a/casr/src/bin/casr-ubsan.rs b/casr/src/bin/casr-ubsan.rs index ffc1e1a7..42200ec5 100644 --- a/casr/src/bin/casr-ubsan.rs +++ b/casr/src/bin/casr-ubsan.rs @@ -238,7 +238,7 @@ fn main() -> Result<()> { .default_value("0") .value_name("SECONDS") .help("Timeout (in seconds) for target execution, 0 value means that timeout is disabled") - .value_parser(clap::value_parser!(u64).range(0..)) + .value_parser(clap::value_parser!(u64)) ) .arg( Arg::new("input") diff --git a/casr/tests/tests.rs b/casr/tests/tests.rs index e910d309..397d8a54 100644 --- a/casr/tests/tests.rs +++ b/casr/tests/tests.rs @@ -3867,7 +3867,7 @@ fn test_casr_ubsan() { let test_dir = abs_path("tests/tmp_tests_casr/test_casr_ubsan"); let _ = fs::remove_dir_all(&test_dir); - let _ = copy_dir(work_dir, &test_dir); + let _ = copy_dir(work_dir, &test_dir).unwrap(); let paths = [ abs_path("tests/tmp_tests_casr/test_casr_ubsan/test_ubsan.cpp"), @@ -4367,7 +4367,7 @@ fn test_casr_san_python_df() { let work_dir = abs_path("tests/casr_tests/python"); let test_dir = abs_path("tests/tmp_tests_casr/test_casr_san_python_df"); - let _ = copy_dir(work_dir, &test_dir); + let _ = copy_dir(work_dir, &test_dir).unwrap(); let paths = [ abs_path("tests/tmp_tests_casr/test_casr_san_python_df/cpp_module.cpp"), @@ -4464,7 +4464,7 @@ fn test_casr_san_atheris_df() { let work_dir = abs_path("tests/casr_tests/python"); let test_dir = abs_path("tests/tmp_tests_casr/test_casr_san_atheris_df"); - let _ = copy_dir(work_dir, &test_dir); + let _ = copy_dir(work_dir, &test_dir).unwrap(); let paths = [ abs_path("tests/tmp_tests_casr/test_casr_san_atheris_df/cpp_module.cpp"), @@ -4565,7 +4565,7 @@ fn test_casr_python_call_san_df() { let work_dir = abs_path("tests/casr_tests/python"); let test_dir = abs_path("tests/tmp_tests_casr/test_casr_python_call_san_df"); - let _ = copy_dir(work_dir, &test_dir); + let _ = copy_dir(work_dir, &test_dir).unwrap(); let paths = [ abs_path("tests/tmp_tests_casr/test_casr_python_call_san_df/cpp_module.cpp"), @@ -4925,7 +4925,7 @@ fn test_casr_js_native() { let test_dir = abs_path("tests/tmp_tests_casr/test_casr_js_native"); let _ = std::fs::remove_dir_all(&test_dir); - let _ = copy_dir(work_dir, &test_dir); + let _ = copy_dir(work_dir, &test_dir).unwrap(); let paths = [ abs_path("tests"), @@ -5050,7 +5050,7 @@ fn test_casr_js_native_jsfuzz() { let test_dir = abs_path("tests/tmp_tests_casr/test_casr_js_native_jsfuzz"); let _ = std::fs::remove_dir_all(&test_dir); - let _ = copy_dir(work_dir, &test_dir); + let _ = copy_dir(work_dir, &test_dir).unwrap(); let paths = [ abs_path("tests"), @@ -5175,7 +5175,7 @@ fn test_casr_js_native_jazzer() { let test_dir = abs_path("tests/tmp_tests_casr/test_casr_js_native_jazzer"); let _ = std::fs::remove_dir_all(&test_dir); - let _ = copy_dir(work_dir, &test_dir); + let _ = copy_dir(work_dir, &test_dir).unwrap(); let paths = [ abs_path("tests"), @@ -5740,8 +5740,8 @@ fn test_casr_afl_csharp() { let _ = fs::remove_dir_all(&paths[1]); let _ = fs::create_dir(abs_path("tests/tmp_tests_casr")); - let _ = copy_dir(&paths[2], &paths[4]); - let _ = copy_dir(&paths[3], &paths[5]); + 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."); }; @@ -5840,8 +5840,8 @@ fn test_casr_afl_csharp_ignore_cmd() { let _ = fs::remove_dir_all(&paths[1]); let _ = fs::create_dir(abs_path("tests/tmp_tests_casr")); - let _ = copy_dir(&paths[2], &paths[4]); - let _ = copy_dir(&paths[3], &paths[5]); + 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."); }; From 410187927dd734548cfece2f404e0ba1e69db5c0 Mon Sep 17 00:00:00 2001 From: headshog Date: Tue, 2 Apr 2024 14:01:43 +0300 Subject: [PATCH 32/38] Fix casr-afl and clippy --- casr/src/bin/casr-afl.rs | 40 +++++++++++++--------------------- casr/src/bin/casr-core.rs | 2 ++ casr/src/bin/casr-csharp.rs | 2 +- casr/src/bin/casr-libfuzzer.rs | 2 +- libcasr/src/cpp.rs | 7 ++---- libcasr/src/js.rs | 4 +--- libcasr/src/rust.rs | 4 +--- 7 files changed, 23 insertions(+), 38 deletions(-) diff --git a/casr/src/bin/casr-afl.rs b/casr/src/bin/casr-afl.rs index 2aced504..2c0f158b 100644 --- a/casr/src/bin/casr-afl.rs +++ b/casr/src/bin/casr-afl.rs @@ -109,6 +109,7 @@ fn main() -> Result<()> { // Init log. util::initialize_logging(&matches); + let mut is_casr_gdb = true; let mut args = if let Some(argv) = matches.get_many::("ARGS") { argv.cloned().collect() } else { @@ -119,16 +120,6 @@ fn main() -> Result<()> { bail!("ARGS is empty, but \"ignore-cmdline\" option is provided."); } - // Get tool. - let mut tool = if matches.get_flag("ignore-cmdline") - && (args[0].ends_with("dotnet") || args[0].ends_with("mono")) - { - "casr-csharp" - } else { - "casr-gdb" - }; - let tool_path = util::get_path(tool)?; - // Get all crashes. let mut crashes: HashMap = HashMap::new(); for node_dir in fs::read_dir(matches.get_one::("input").unwrap())? { @@ -141,27 +132,26 @@ fn main() -> Result<()> { let mut crash_info = casr::triage::CrashInfo { ..Default::default() }; - if matches.get_flag("ignore-cmdline") { - crash_info.casr_tool = tool_path.clone(); - crash_info.target_args = args.clone() + crash_info.target_args = if matches.get_flag("ignore-cmdline") { + args.clone() } else { let cmdline_path = path.join("cmdline"); if let Ok(cmdline) = fs::read_to_string(&cmdline_path) { - let cmd_args: Vec = - cmdline.split_whitespace().map(|s| s.to_string()).collect(); - if cmd_args[0].ends_with("dotnet") || cmd_args[0].ends_with("mono") { - tool = "casr-csharp"; - crash_info.casr_tool = util::get_path("casr-csharp")?; - } else { - tool = "casr-gdb"; - crash_info.casr_tool = util::get_path("casr-gdb")?; - } - crash_info.target_args = cmd_args; + cmdline.split_whitespace().map(|s| s.to_string()).collect() } else { error!("Couldn't read {}.", cmdline_path.display()); continue; } }; + crash_info.casr_tool = if !crash_info.target_args.is_empty() + && (crash_info.target_args[0].ends_with("dotnet") + || crash_info.target_args[0].ends_with("mono")) + { + is_casr_gdb = false; + util::get_path("casr-csharp")? + } else { + util::get_path("casr-gdb")? + }; crash_info.at_index = crash_info .target_args .iter() @@ -170,7 +160,7 @@ fn main() -> Result<()> { .map(|x| x + 1); // When we triage crashes for binaries, use casr-san. - if tool == "casr-gdb" { + if is_casr_gdb { if let Some(target) = crash_info.target_args.first() { match util::symbols_list(Path::new(target)) { Ok(list) => { @@ -205,7 +195,7 @@ fn main() -> Result<()> { } } - if matches.get_flag("ignore-cmdline") || tool != "casr-gdb" { + if matches.get_flag("ignore-cmdline") || !is_casr_gdb { args = Vec::new(); } diff --git a/casr/src/bin/casr-core.rs b/casr/src/bin/casr-core.rs index 7f42e272..dcde810a 100644 --- a/casr/src/bin/casr-core.rs +++ b/casr/src/bin/casr-core.rs @@ -302,6 +302,7 @@ fn main() -> Result<()> { // Save report. if let Ok(mut file) = OpenOptions::new() .create(true) + .truncate(true) .write(true) .open(&report_path) { @@ -319,6 +320,7 @@ fn check_lock() -> Result { project_dir.push("Casr.lock"); let file = OpenOptions::new() .create(true) + .truncate(true) .write(true) .open(project_dir)?; let fd = file.as_raw_fd(); diff --git a/casr/src/bin/casr-csharp.rs b/casr/src/bin/casr-csharp.rs index d24a6d48..5f0fef2a 100644 --- a/casr/src/bin/casr-csharp.rs +++ b/casr/src/bin/casr-csharp.rs @@ -72,7 +72,7 @@ fn main() -> Result<()> { ) .get_matches(); - init_ignored_frames!("csharp", "cpp"); //TODO + init_ignored_frames!("csharp", "cpp"); if let Some(path) = matches.get_one::("ignore") { util::add_custom_ignored_frames(path)?; } diff --git a/casr/src/bin/casr-libfuzzer.rs b/casr/src/bin/casr-libfuzzer.rs index 9c1e83fc..bb58cfd4 100644 --- a/casr/src/bin/casr-libfuzzer.rs +++ b/casr/src/bin/casr-libfuzzer.rs @@ -147,7 +147,7 @@ fn main() -> Result<()> { let tool_path = util::get_path(tool)?; if !gdb_args.is_empty() && tool != "casr-gdb" && tool != "casr-san" { - bail!("casr-gdb-args option is provided with incompatible tool (casr-python, casr-java or casr-js)."); + bail!("casr-gdb-args option is provided with incompatible tool. This option can be used with casr-san or casr-gdb."); } // Get input file argument index. diff --git a/libcasr/src/cpp.rs b/libcasr/src/cpp.rs index 42de96f6..fc94853b 100644 --- a/libcasr/src/cpp.rs +++ b/libcasr/src/cpp.rs @@ -15,12 +15,9 @@ impl Exception for CppException { .collect(); let rexception = Regex::new(r"terminate called after throwing an instance of (.+)").unwrap(); - let Some(pos) = stderr_list + let pos = stderr_list .iter() - .position(|line| rexception.is_match(line)) - else { - return None; - }; + .position(|line| rexception.is_match(line))?; let instance = rexception .captures(&stderr_list[pos]) .unwrap() diff --git a/libcasr/src/js.rs b/libcasr/src/js.rs index 31b5167e..6287b51f 100644 --- a/libcasr/src/js.rs +++ b/libcasr/src/js.rs @@ -12,9 +12,7 @@ pub struct JsException; impl Exception for JsException { fn parse_exception(stderr: &str) -> Option { let rexception = Regex::new(r"(?m)^.*?(\S*Error):(?:\s+(.*))?$").unwrap(); - let Some(captures) = rexception.captures(stderr) else { - return None; - }; + let captures = rexception.captures(stderr)?; let error_type = captures.get(1).unwrap().as_str(); let message = if let Some(message) = captures.get(2) { message.as_str() diff --git a/libcasr/src/rust.rs b/libcasr/src/rust.rs index b6689b38..5ba76b66 100644 --- a/libcasr/src/rust.rs +++ b/libcasr/src/rust.rs @@ -13,9 +13,7 @@ pub struct RustPanic; impl Exception for RustPanic { fn parse_exception(stderr: &str) -> Option { let rexception = Regex::new(r"thread '.+?' panicked at (?:'(.*)'|.+?:\n(.*))").unwrap(); - let Some(captures) = rexception.captures(stderr) else { - return None; - }; + let captures = rexception.captures(stderr)?; let message = if let Some(message) = captures.get(1) { message.as_str() } else { From 0a6f36d85a4ea11b675049f640801f3813f8dcff Mon Sep 17 00:00:00 2001 From: headshog Date: Tue, 2 Apr 2024 19:45:25 +0300 Subject: [PATCH 33/38] fix docs and csharp tests --- README.md | 4 ++-- .../test_casr_csharp.cs | 0 .../test_casr_csharp.csproj | 0 casr/tests/tests.rs | 4 ++-- docs/usage.md | 20 +++++++++---------- 5 files changed, 14 insertions(+), 14 deletions(-) rename casr/tests/casr_tests/csharp/{ => test_casr_csharp}/test_casr_csharp.cs (100%) rename casr/tests/casr_tests/csharp/{ => test_casr_csharp}/test_casr_csharp.csproj (100%) diff --git a/README.md b/README.md index 8e9efb55..2b900bb2 100644 --- a/README.md +++ b/README.md @@ -167,7 +167,7 @@ Create report from JavaScript: Create report from C#: - $ casr-csharp -o csharp.casrep -- dotnet run --project casr/tests/casr_tests/csharp/test_casr_csharp.csproj + $ casr-csharp -o csharp.casrep -- dotnet run --project casr/tests/casr_tests/csharp/test_casr_csharp/test_casr_csharp.csproj View report: @@ -205,7 +205,7 @@ Triage crashes after Sharpfuzz fuzzing with casr-afl: $ 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 -o /tmp/test_casr_afl_csharp/bin + $ dotnet publish /tmp/test_casr_afl_csharp/test_casr_afl_csharp.csproj -o /tmp/test_casr_afl_csharp/bin $ 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 @@ diff --git a/casr/tests/casr_tests/csharp/test_casr_csharp.cs b/casr/tests/casr_tests/csharp/test_casr_csharp/test_casr_csharp.cs similarity index 100% rename from casr/tests/casr_tests/csharp/test_casr_csharp.cs rename to casr/tests/casr_tests/csharp/test_casr_csharp/test_casr_csharp.cs diff --git a/casr/tests/casr_tests/csharp/test_casr_csharp.csproj b/casr/tests/casr_tests/csharp/test_casr_csharp/test_casr_csharp.csproj similarity index 100% rename from casr/tests/casr_tests/csharp/test_casr_csharp.csproj rename to casr/tests/casr_tests/csharp/test_casr_csharp/test_casr_csharp.csproj diff --git a/casr/tests/tests.rs b/casr/tests/tests.rs index 397d8a54..09725f01 100644 --- a/casr/tests/tests.rs +++ b/casr/tests/tests.rs @@ -5672,8 +5672,8 @@ fn test_casr_libfuzzer_jazzer_js_xml2js() { #[cfg(target_arch = "x86_64")] fn test_casr_csharp() { let paths = [ - abs_path("tests/casr_tests/csharp/test_casr_csharp.cs"), - abs_path("tests/casr_tests/csharp/test_casr_csharp.csproj"), + abs_path("tests/casr_tests/csharp/test_casr_csharp/test_casr_csharp.cs"), + abs_path("tests/casr_tests/csharp/test_casr_csharp/test_casr_csharp.csproj"), abs_path("tests/tmp_tests_casr/test_casr_csharp"), abs_path("tests/tmp_tests_casr/test_casr_csharp/test_casr_csharp.cs"), abs_path("tests/tmp_tests_casr/test_casr_csharp/test_casr_csharp.csproj"), diff --git a/docs/usage.md b/docs/usage.md index 082b580b..9227190a 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -28,7 +28,7 @@ Create CASR reports (.casrep) from gdb execution Usage: casr-gdb [OPTIONS] <--stdout|--output > -- ... Arguments: - [ARGS]... Add "-- ./binary " to run executable + ... Add "-- ./binary " to run executable Options: -o, --output Path to save report. Path can be a directory, then report @@ -55,7 +55,7 @@ Create CASR reports (.casrep) from AddressSanitizer reports Usage: casr-san [OPTIONS] <--stdout|--output > -- ... Arguments: - [ARGS]... Add "-- ./binary " to run executable + ... Add "-- ./binary " to run executable Options: -o, --output Path to save report. Path can be a directory, then report @@ -93,7 +93,7 @@ Triage errors found by UndefinedBehaviorSanitizer and create CASR reports (.casr Usage: casr-ubsan [OPTIONS] --input ... --output -- ... Arguments: - [ARGS]... Add "-- " to run + ... Add "-- " to run Options: -l, --log-level Logging level [default: info] [possible values: info, @@ -130,7 +130,7 @@ Create CASR reports (.casrep) from python reports Usage: casr-python [OPTIONS] <--stdout|--output > -- ... Arguments: - [ARGS]... Add "-- " to run + ... Add "-- " to run Options: -o, --output Path to save report. Path can be a directory, then report @@ -156,7 +156,7 @@ Create CASR reports (.casrep) from java reports Usage: casr-java [OPTIONS] <--stdout|--output > -- ... Arguments: - [ARGS]... Add "-- " to run + ... Add "-- " to run Options: -o, --output Path to save report. Path can be a directory, then report @@ -185,7 +185,7 @@ Create CASR reports (.casrep) from JavaScript crash reports Usage: casr-js [OPTIONS] <--stdout|--output > -- ... Arguments: - [ARGS]... Add "-- " to run + ... Add "-- " to run Options: -o, --output Path to save report. Path can be a directory, then report @@ -212,7 +212,7 @@ Create CASR reports (.casrep) from C# reports Usage: casr-csharp [OPTIONS] <--stdout|--output > -- ... Arguments: - [ARGS]... Add "-- " to run + ... Add "-- " to run Options: -o, --output Path to save report. Path can be a directory, then report name @@ -228,7 +228,7 @@ Create CASR reports (.casrep) from C# reports Run casr-csharp: - $ casr-csharp -o csharp.casrep -- dotnet run --project casr/tests/casr_tests/csharp/test_casr_csharp.csproj + $ casr-csharp -o csharp.casrep -- dotnet run --project casr/tests/casr_tests/csharp/test_casr_csharp/test_casr_csharp.csproj ## casr-core @@ -552,7 +552,7 @@ Sharpfuzz example: $ 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 -o /tmp/test_casr_afl_csharp/bin + $ dotnet publish /tmp/test_casr_afl_csharp/test_casr_afl_csharp.csproj -o /tmp/test_casr_afl_csharp/bin $ casr-afl -i casr/tests/casr_tests/casrep/afl-out-sharpfuzz -o casr/tests/tmp_tests_casr/casr_afl_csharp_out Sharpfuzz example (with --ignore-cmdline): @@ -573,7 +573,7 @@ Triage crashes found by libFuzzer based fuzzer Usage: casr-libfuzzer [OPTIONS] --output -- ... Arguments: - [ARGS]... Add "-- ./fuzz_target " + ... Add "-- ./fuzz_target " Options: -l, --log-level From defe5f7f080c904bbdd751133271877740864583 Mon Sep 17 00:00:00 2001 From: headshog Date: Tue, 2 Apr 2024 20:56:04 +0300 Subject: [PATCH 34/38] add gitignore to csharp artifacts --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index a89614ff..b3cec92b 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,8 @@ Cargo.lock */target */Cargo.lock */tests/tmp_tests_casr +*/tests/casr_tests/csharp/*/bin +*/tests/casr_tests/csharp/*/obj *.swp node_modules */node_modules/* From b3cafbbf8efbb2522dd58787f78eeebacabcfd4a Mon Sep 17 00:00:00 2001 From: headshog Date: Wed, 3 Apr 2024 13:04:03 +0300 Subject: [PATCH 35/38] fix csharp report --- casr/src/bin/casr-csharp.rs | 7 +++- csharp.casrep | 83 +++++++++++++++++++++++++++++++++++++ libcasr/src/csharp.rs | 3 +- 3 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 csharp.casrep diff --git a/casr/src/bin/casr-csharp.rs b/casr/src/bin/casr-csharp.rs index 5f0fef2a..9b2634d9 100644 --- a/casr/src/bin/casr-csharp.rs +++ b/casr/src/bin/casr-csharp.rs @@ -122,7 +122,12 @@ fn main() -> Result<()> { csharp_stderr.split('\n').map(|l| l.to_string()).collect(); let re = Regex::new(r"^Unhandled [Ee]xception(?::\n|\. ).*").unwrap(); if let Some(start) = csharp_stderr_list.iter().position(|x| re.is_match(x)) { - report.csharp_report = csharp_stderr_list[start..].to_vec(); + let end = csharp_stderr_list[start..] + .iter() + .rposition(|x| !x.is_empty()) + .unwrap() + + 1; + report.csharp_report = csharp_stderr_list[start..end].to_vec(); let report_str = report.csharp_report.join("\n"); report.stacktrace = CSharpStacktrace::extract_stacktrace(&report_str)?; if let Some(exception) = CSharpException::parse_exception(&report_str) { diff --git a/csharp.casrep b/csharp.casrep new file mode 100644 index 00000000..6f578133 --- /dev/null +++ b/csharp.casrep @@ -0,0 +1,83 @@ +{ + "Date": "2024-04-03T12:45:38.788982+03:00", + "Uname": "Linux nick 5.15.0-79-generic #86~20.04.2-Ubuntu SMP Mon Jul 17 23:27:17 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux", + "OS": "Ubuntu", + "OSRelease": "20.04", + "Architecture": "amd64", + "ExecutablePath": "casr/tests/casr_tests/csharp/test_casr_csharp/test_casr_csharp.csproj", + "ProcEnviron": [ + "GJS_DEBUG_TOPICS=JS ERROR;JS LOG", + "IM_CONFIG_PHASE=1", + "COLORTERM=truecolor", + "QT_IM_MODULE=ibus", + "USER=headshog", + "HOME=/home/headshog", + "GJS_DEBUG_OUTPUT=stderr", + "PWD=/home/headshog/casr", + "_=/home/headshog/casr/./target/debug/casr-csharp", + "JOURNAL_STREAM=8:2212765", + "WINDOWPATH=2", + "MANAGERPID=2410990", + "PATH=/home/headshog/.cargo/bin:/home/headshog/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/home/headshog/.dotnet/tools", + "XMODIFIERS=@im=ibus", + "QT_ACCESSIBILITY=1", + "SHELL=/bin/zsh", + "VTE_VERSION=6003", + "INVOCATION_ID=77de27253478465ba8558472de9846f7", + "SHLVL=1", + "DISPLAY=:0", + "TERM=xterm-256color", + "GDMSESSION=ubuntu", + "OLDPWD=/home/headshog/casr/target", + "ZSH=/home/headshog/.oh-my-zsh", + "PAGER=less", + "LESS=-R", + "LSCOLORS=Gxfxcxdxbxegedabagacad" + ], + "ProcCmdline": "dotnet run --project casr/tests/casr_tests/csharp/test_casr_csharp/test_casr_csharp.csproj", + "Stdin": "", + "ProcStatus": [], + "ProcMaps": [], + "ProcFiles": [], + "NetworkConnections": [], + "CrashSeverity": { + "Type": "NOT_EXPLOITABLE", + "ShortDescription": "System.ArgumentException", + "Description": "Parameter cannot be null", + "Explanation": "" + }, + "Stacktrace": [ + "at Program.f2() in /home/headshog/casr/casr/tests/casr_tests/csharp/test_casr_csharp/test_casr_csharp.cs:line 14", + "at Program.f1() in /home/headshog/casr/casr/tests/casr_tests/csharp/test_casr_csharp/test_casr_csharp.cs:line 10", + "at Program.Main(String[] args) in /home/headshog/casr/casr/tests/casr_tests/csharp/test_casr_csharp/test_casr_csharp.cs:line 6" + ], + "Registers": {}, + "Disassembly": [], + "Package": "", + "PackageVersion": "", + "PackageArchitecture": "", + "PackageDescription": "", + "AsanReport": [], + "UbsanReport": [], + "PythonReport": [], + "GoReport": [], + "JavaReport": [], + "RustReport": [], + "JsReport": [], + "CSharpReport": [ + "Unhandled exception. System.ArgumentException: Parameter cannot be null", + " at Program.f2() in /home/headshog/casr/casr/tests/casr_tests/csharp/test_casr_csharp/test_casr_csharp.cs:line 14", + " at Program.f1() in /home/headshog/casr/casr/tests/casr_tests/csharp/test_casr_csharp/test_casr_csharp.cs:line 10", + " at Program.Main(String[] args) in /home/headshog/casr/casr/tests/casr_tests/csharp/test_casr_csharp/test_casr_csharp.cs:line 6" + ], + "CrashLine": "/home/headshog/casr/casr/tests/casr_tests/csharp/test_casr_csharp/test_casr_csharp.cs:14", + "Source": [ + " 10 f2();", + " 11 }", + " 12 ", + " 13 public static void f2() {", + "--->14 throw new ArgumentException(\"Parameter cannot be null\");", + " 15 }", + " 16 }" + ] +} \ No newline at end of file diff --git a/libcasr/src/csharp.rs b/libcasr/src/csharp.rs index 194f50f5..dabf71f4 100644 --- a/libcasr/src/csharp.rs +++ b/libcasr/src/csharp.rs @@ -137,9 +137,10 @@ impl Exception for CSharpException { .split_once(": ")?; Some(ExecutionClass { + severity: "NOT_EXPLOITABLE".to_string(), short_description: exception.to_string(), description: message.to_string(), - ..ExecutionClass::default() + explanation: "".to_string(), }) } } From dee63cea18f3bc86c3a16ace62221cd50589cdbd Mon Sep 17 00:00:00 2001 From: headshog Date: Wed, 3 Apr 2024 17:02:03 +0300 Subject: [PATCH 36/38] fix csharp test and readme use cases --- README.md | 2 +- casr/tests/tests.rs | 2 +- csharp.casrep | 83 --------------------------------------------- 3 files changed, 2 insertions(+), 85 deletions(-) delete mode 100644 csharp.casrep diff --git a/README.md b/README.md index 2b900bb2..4903e4b8 100644 --- a/README.md +++ b/README.md @@ -205,7 +205,7 @@ Triage crashes after Sharpfuzz fuzzing with casr-afl: $ 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 -o /tmp/test_casr_afl_csharp/bin + $ 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 -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 @@ diff --git a/casr/tests/tests.rs b/casr/tests/tests.rs index 09725f01..fb08fd4d 100644 --- a/casr/tests/tests.rs +++ b/casr/tests/tests.rs @@ -5713,7 +5713,7 @@ fn test_casr_csharp() { .to_string(); assert_eq!(3, report["Stacktrace"].as_array().unwrap().iter().count()); - assert_eq!(severity_type, "UNDEFINED"); + assert_eq!(severity_type, "NOT_EXPLOITABLE"); assert_eq!(severity_desc, "System.ArgumentException"); assert!(report["CrashLine"] .as_str() diff --git a/csharp.casrep b/csharp.casrep deleted file mode 100644 index 6f578133..00000000 --- a/csharp.casrep +++ /dev/null @@ -1,83 +0,0 @@ -{ - "Date": "2024-04-03T12:45:38.788982+03:00", - "Uname": "Linux nick 5.15.0-79-generic #86~20.04.2-Ubuntu SMP Mon Jul 17 23:27:17 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux", - "OS": "Ubuntu", - "OSRelease": "20.04", - "Architecture": "amd64", - "ExecutablePath": "casr/tests/casr_tests/csharp/test_casr_csharp/test_casr_csharp.csproj", - "ProcEnviron": [ - "GJS_DEBUG_TOPICS=JS ERROR;JS LOG", - "IM_CONFIG_PHASE=1", - "COLORTERM=truecolor", - "QT_IM_MODULE=ibus", - "USER=headshog", - "HOME=/home/headshog", - "GJS_DEBUG_OUTPUT=stderr", - "PWD=/home/headshog/casr", - "_=/home/headshog/casr/./target/debug/casr-csharp", - "JOURNAL_STREAM=8:2212765", - "WINDOWPATH=2", - "MANAGERPID=2410990", - "PATH=/home/headshog/.cargo/bin:/home/headshog/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/home/headshog/.dotnet/tools", - "XMODIFIERS=@im=ibus", - "QT_ACCESSIBILITY=1", - "SHELL=/bin/zsh", - "VTE_VERSION=6003", - "INVOCATION_ID=77de27253478465ba8558472de9846f7", - "SHLVL=1", - "DISPLAY=:0", - "TERM=xterm-256color", - "GDMSESSION=ubuntu", - "OLDPWD=/home/headshog/casr/target", - "ZSH=/home/headshog/.oh-my-zsh", - "PAGER=less", - "LESS=-R", - "LSCOLORS=Gxfxcxdxbxegedabagacad" - ], - "ProcCmdline": "dotnet run --project casr/tests/casr_tests/csharp/test_casr_csharp/test_casr_csharp.csproj", - "Stdin": "", - "ProcStatus": [], - "ProcMaps": [], - "ProcFiles": [], - "NetworkConnections": [], - "CrashSeverity": { - "Type": "NOT_EXPLOITABLE", - "ShortDescription": "System.ArgumentException", - "Description": "Parameter cannot be null", - "Explanation": "" - }, - "Stacktrace": [ - "at Program.f2() in /home/headshog/casr/casr/tests/casr_tests/csharp/test_casr_csharp/test_casr_csharp.cs:line 14", - "at Program.f1() in /home/headshog/casr/casr/tests/casr_tests/csharp/test_casr_csharp/test_casr_csharp.cs:line 10", - "at Program.Main(String[] args) in /home/headshog/casr/casr/tests/casr_tests/csharp/test_casr_csharp/test_casr_csharp.cs:line 6" - ], - "Registers": {}, - "Disassembly": [], - "Package": "", - "PackageVersion": "", - "PackageArchitecture": "", - "PackageDescription": "", - "AsanReport": [], - "UbsanReport": [], - "PythonReport": [], - "GoReport": [], - "JavaReport": [], - "RustReport": [], - "JsReport": [], - "CSharpReport": [ - "Unhandled exception. System.ArgumentException: Parameter cannot be null", - " at Program.f2() in /home/headshog/casr/casr/tests/casr_tests/csharp/test_casr_csharp/test_casr_csharp.cs:line 14", - " at Program.f1() in /home/headshog/casr/casr/tests/casr_tests/csharp/test_casr_csharp/test_casr_csharp.cs:line 10", - " at Program.Main(String[] args) in /home/headshog/casr/casr/tests/casr_tests/csharp/test_casr_csharp/test_casr_csharp.cs:line 6" - ], - "CrashLine": "/home/headshog/casr/casr/tests/casr_tests/csharp/test_casr_csharp/test_casr_csharp.cs:14", - "Source": [ - " 10 f2();", - " 11 }", - " 12 ", - " 13 public static void f2() {", - "--->14 throw new ArgumentException(\"Parameter cannot be null\");", - " 15 }", - " 16 }" - ] -} \ No newline at end of file From d85041dd46434920ae18b96e360016da71d7409d Mon Sep 17 00:00:00 2001 From: headshog Date: Wed, 3 Apr 2024 17:49:34 +0300 Subject: [PATCH 37/38] fix usage usecases and add strip-path for casr-csharp --- casr/src/bin/casr-csharp.rs | 15 ++++++++++++++- docs/usage.md | 4 ++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/casr/src/bin/casr-csharp.rs b/casr/src/bin/casr-csharp.rs index 9b2634d9..ce26ac2d 100644 --- a/casr/src/bin/casr-csharp.rs +++ b/casr/src/bin/casr-csharp.rs @@ -62,6 +62,14 @@ fn main() -> Result<()> { .value_name("FILE") .help("File with regular expressions for functions and file paths that should be ignored"), ) + .arg( + Arg::new("strip-path") + .long("strip-path") + .env("CASR_STRIP_PATH") + .action(ArgAction::Set) + .value_name("PREFIX") + .help("Path prefix to strip from stacktrace and crash line"), + ) .arg( Arg::new("ARGS") .action(ArgAction::Set) @@ -135,7 +143,8 @@ fn main() -> Result<()> { } } - if let Ok(crash_line) = CSharpStacktrace::parse_stacktrace(&report.stacktrace)?.crash_line() { + let stacktrace = CSharpStacktrace::parse_stacktrace(&report.stacktrace)?; + if let Ok(crash_line) = stacktrace.crash_line() { report.crashline = crash_line.to_string(); if let CrashLine::Source(debug) = crash_line { if let Some(sources) = CrashReport::sources(&debug) { @@ -144,6 +153,10 @@ fn main() -> Result<()> { } } + if let Some(path) = matches.get_one::("strip-path") { + util::strip_paths(&mut report, &stacktrace, path); + } + //Output report. util::output_report(&report, &matches, &argv) } diff --git a/docs/usage.md b/docs/usage.md index 9227190a..8a3a23b7 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -552,14 +552,14 @@ Sharpfuzz example: $ 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 -o /tmp/test_casr_afl_csharp/bin + $ 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 -o casr/tests/tmp_tests_casr/casr_afl_csharp_out Sharpfuzz example (with --ignore-cmdline): $ 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 build /tmp/test_casr_afl_csharp/test_casr_afl_csharp.csproj + $ 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 From 402e54036467a89025992d2c4cc4a3c9366f0a0c Mon Sep 17 00:00:00 2001 From: Alexey Vishnyakov Date: Wed, 3 Apr 2024 20:15:27 +0300 Subject: [PATCH 38/38] Fix casr-core append --- casr/src/bin/casr-core.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/casr/src/bin/casr-core.rs b/casr/src/bin/casr-core.rs index dcde810a..4854970b 100644 --- a/casr/src/bin/casr-core.rs +++ b/casr/src/bin/casr-core.rs @@ -320,8 +320,7 @@ fn check_lock() -> Result { project_dir.push("Casr.lock"); let file = OpenOptions::new() .create(true) - .truncate(true) - .write(true) + .append(true) .open(project_dir)?; let fd = file.as_raw_fd(); flock(fd, FlockArg::LockExclusive).unwrap();