Skip to content

Commit 33d8afd

Browse files
committed
Make common casr tool draft
1 parent 4bc482c commit 33d8afd

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+3194
-2287
lines changed

casr/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ simplelog = "0.12"
2121
cursive = { version = "0.21", default-features = false, features = ["termion-backend"] }
2222
cursive_tree_view = "0.9"
2323
gdb-command = "0.7"
24-
nix = { version = "0.29", features = ["fs"] }
24+
nix = { version = "0.29", features = ["fs", "process"] }
2525
rayon = "1.10"
2626
num_cpus = "1.16"
2727
is_executable = "1.0"

casr/src/bin/casr-afl.rs

Lines changed: 26 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
1-
use casr::triage::{CrashInfo, fuzzing_crash_triage_pipeline};
2-
use casr::util;
1+
use casr::{
2+
mode::{DynMode, csharp::CSharpMode},
3+
triage::{CrashInfo, fuzzing_crash_triage_pipeline},
4+
util,
5+
};
6+
7+
use std::collections::HashMap;
8+
use std::fs;
9+
use std::path::{Path, PathBuf};
310

411
use anyhow::{Result, bail};
512
use clap::{
@@ -8,10 +15,6 @@ use clap::{
815
};
916
use log::error;
1017

11-
use std::collections::HashMap;
12-
use std::fs;
13-
use std::path::{Path, PathBuf};
14-
1518
fn main() -> Result<()> {
1619
let matches = clap::Command::new("casr-afl")
1720
.version(clap::crate_version!())
@@ -26,11 +29,12 @@ fn main() -> Result<()> {
2629
.value_parser(["info", "debug"])
2730
.help("Logging level")
2831
)
29-
.arg(Arg::new("jobs")
30-
.long("jobs")
31-
.short('j')
32-
.action(ArgAction::Set)
33-
.help("Number of parallel jobs for generating CASR reports [default: half of cpu cores]")
32+
.arg(
33+
Arg::new("jobs")
34+
.long("jobs")
35+
.short('j')
36+
.action(ArgAction::Set)
37+
.help("Number of parallel jobs for generating CASR reports [default: half of cpu cores]")
3438
.value_parser(clap::value_parser!(u32).range(1..)))
3539
.arg(
3640
Arg::new("timeout")
@@ -109,7 +113,7 @@ fn main() -> Result<()> {
109113
.value_name("HINT")
110114
.action(ArgAction::Set)
111115
.default_value("auto")
112-
.value_parser(["auto", "gdb", "san", "csharp"])
116+
.value_parser(["auto", "gdb", "san", "asan", "msan", "csharp"])
113117
.help("Hint to force run casr-HINT tool to analyze crashes")
114118
)
115119
.arg(
@@ -134,7 +138,10 @@ fn main() -> Result<()> {
134138
Vec::new()
135139
};
136140

137-
let hint = matches.get_one::<String>("hint").unwrap();
141+
let hint = matches
142+
.get_one::<String>("hint")
143+
.as_ref()
144+
.map(|x| x.as_str());
138145
let input = matches.get_one::<PathBuf>("input").unwrap();
139146
let afl_dir = input.join("crashes").is_dir();
140147
let ignore_cmdline = matches.get_flag("ignore-cmdline") || afl_dir;
@@ -173,44 +180,20 @@ fn main() -> Result<()> {
173180
continue;
174181
}
175182
};
176-
crash_info.casr_tool = if hint == "csharp"
177-
|| hint == "auto"
178-
&& !crash_info.target_args.is_empty()
179-
&& (crash_info.target_args[0].ends_with("dotnet")
180-
|| crash_info.target_args[0].ends_with("mono"))
181-
{
183+
crash_info.mode = DynMode::try_from((hint, &crash_info.target_args))?;
184+
if crash_info.mode.is_mode::<CSharpMode>() {
182185
is_casr_gdb = false;
183-
util::get_path("casr-csharp")?
184-
} else {
185-
util::get_path("casr-gdb")?
186-
};
186+
}
187187
crash_info.at_index = crash_info
188188
.target_args
189189
.iter()
190190
.skip(1)
191191
.position(|s| s.contains("@@"))
192192
.map(|x| x + 1);
193193

194-
// When we triage crashes for binaries, use casr-san.
195-
if hint == "san" || hint == "auto" && is_casr_gdb {
196-
if let Some(target) = crash_info.target_args.first() {
197-
match util::symbols_list(Path::new(target)) {
198-
Ok(list) => {
199-
if list.contains("__asan") {
200-
crash_info
201-
.casr_tool
202-
.clone_from(&(util::get_path("casr-san")?))
203-
}
204-
}
205-
Err(e) => {
206-
error!("{e}");
207-
continue;
208-
}
209-
}
210-
} else {
211-
error!("Cmdline is empty. Path: {:?}", path.join("cmdline"));
212-
continue;
213-
}
194+
if is_casr_gdb && crash_info.target_args.is_empty() {
195+
error!("Cmdline is empty. Path: {:?}", path.join("cmdline"));
196+
continue;
214197
}
215198

216199
// Push crash paths.

casr/src/bin/casr-cli.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1013,10 +1013,10 @@ fn print_summary(dir: &Path, unique_crash_line: bool, strip_path: Option<&String
10131013
println!("==> <{}>", filename.magenta());
10141014
for info in cluster_hash.values() {
10151015
let mut path = info.0.last().unwrap().clone();
1016-
if let Some(prefix) = strip_path {
1017-
if let Ok(stripped) = Path::new(&path).strip_prefix(prefix) {
1018-
path = stripped.display().to_string();
1019-
}
1016+
if let Some(prefix) = strip_path
1017+
&& let Ok(stripped) = Path::new(&path).strip_prefix(prefix)
1018+
{
1019+
path = stripped.display().to_string();
10201020
}
10211021
if ubsan {
10221022
// /path/to/report.casrep: Description: crashline (path:line:column)
@@ -1082,10 +1082,10 @@ fn process_report(
10821082
return None;
10831083
};
10841084
let mut report = report.to_string();
1085-
if let Some(prefix) = strip_path {
1086-
if let Ok(stripped) = Path::new(&report).strip_prefix(prefix) {
1087-
report = stripped.display().to_string();
1088-
}
1085+
if let Some(prefix) = strip_path
1086+
&& let Ok(stripped) = Path::new(&report).strip_prefix(prefix)
1087+
{
1088+
report = stripped.display().to_string();
10891089
}
10901090
let Ok(jreport): Result<Value, _> = serde_json::from_reader(BufReader::new(file)) else {
10911091
return None;

casr/src/bin/casr-core.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ fn main() -> Result<()> {
219219
report.pid = pid;
220220
// Add network connections.
221221
if let Err(error) = report.add_network_connections() {
222-
error!("{}", error);
222+
error!("{error}");
223223
}
224224

225225
file_name_to_save.push_str(&format!("_{}", report.date));

casr/src/bin/casr-csharp.rs

Lines changed: 6 additions & 175 deletions
Original file line numberDiff line numberDiff line change
@@ -1,179 +1,10 @@
1-
use casr::util;
2-
use libcasr::{
3-
csharp::*, exception::Exception, init_ignored_frames, report::CrashReport, stacktrace::*,
4-
};
1+
use anyhow::Result;
52

6-
use anyhow::{Result, bail};
7-
use clap::{Arg, ArgAction, ArgGroup};
8-
use regex::Regex;
9-
use std::path::PathBuf;
10-
use std::process::Command;
3+
use ::casr::{
4+
casr,
5+
mode::{DynMode, csharp::CSharpMode},
6+
};
117

128
fn main() -> Result<()> {
13-
let matches = clap::Command::new("casr-csharp")
14-
.version(clap::crate_version!())
15-
.about("Create CASR reports (.casrep) from C# reports")
16-
.term_width(90)
17-
.arg(
18-
Arg::new("output")
19-
.short('o')
20-
.long("output")
21-
.action(ArgAction::Set)
22-
.value_parser(clap::value_parser!(PathBuf))
23-
.value_name("REPORT")
24-
.help(
25-
"Path to save report. Path can be a directory, then report name is generated",
26-
),
27-
)
28-
.arg(
29-
Arg::new("stdout")
30-
.action(ArgAction::SetTrue)
31-
.long("stdout")
32-
.help("Print CASR report to stdout"),
33-
)
34-
.group(
35-
ArgGroup::new("out")
36-
.args(["stdout", "output"])
37-
.required(true),
38-
)
39-
.arg(
40-
Arg::new("stdin")
41-
.long("stdin")
42-
.action(ArgAction::Set)
43-
.value_parser(clap::value_parser!(PathBuf))
44-
.value_name("FILE")
45-
.help("Stdin file for program"),
46-
)
47-
.arg(
48-
Arg::new("timeout")
49-
.short('t')
50-
.long("timeout")
51-
.action(ArgAction::Set)
52-
.default_value("0")
53-
.value_name("SECONDS")
54-
.help("Timeout (in seconds) for target execution, 0 value means that timeout is disabled")
55-
.value_parser(clap::value_parser!(u64))
56-
)
57-
.arg(
58-
Arg::new("ignore")
59-
.long("ignore")
60-
.action(ArgAction::Set)
61-
.value_parser(clap::value_parser!(PathBuf))
62-
.value_name("FILE")
63-
.help("File with regular expressions for functions and file paths that should be ignored"),
64-
)
65-
.arg(
66-
Arg::new("strip-path")
67-
.long("strip-path")
68-
.env("CASR_STRIP_PATH")
69-
.action(ArgAction::Set)
70-
.value_name("PREFIX")
71-
.help("Path prefix to strip from stacktrace and crash line"),
72-
)
73-
.arg(
74-
Arg::new("ld-preload")
75-
.long("ld-preload")
76-
.env("CASR_PRELOAD")
77-
.action(ArgAction::Set)
78-
.num_args(1..)
79-
.value_name("LIBS")
80-
.value_parser(clap::value_parser!(String))
81-
.help("Set LD_PRELOAD for the target program without disrupting the CASR process itself (both ` ` and `:` are valid delimiter)")
82-
)
83-
.arg(
84-
Arg::new("ARGS")
85-
.action(ArgAction::Set)
86-
.num_args(1..)
87-
.last(true)
88-
.required(true)
89-
.help("Add \"-- <path> <arguments>\" to run"),
90-
)
91-
.get_matches();
92-
93-
init_ignored_frames!("csharp", "cpp");
94-
if let Some(path) = matches.get_one::<PathBuf>("ignore") {
95-
util::add_custom_ignored_frames(path)?;
96-
}
97-
// Get program args.
98-
let argv: Vec<&str> = if let Some(argvs) = matches.get_many::<String>("ARGS") {
99-
argvs.map(|s| s.as_str()).collect()
100-
} else {
101-
bail!("Wrong arguments for starting program");
102-
};
103-
104-
// Check that args are valid.
105-
let Some(pos) = argv
106-
.iter()
107-
.position(|x| x.ends_with(".dll") || x.ends_with(".exe") || x.ends_with(".csproj"))
108-
else {
109-
bail!("dotnet/mono target is not specified by .dll, .exe or .csproj executable.");
110-
};
111-
112-
// Get stdin for target program.
113-
let stdin_file = util::stdin_from_matches(&matches)?;
114-
115-
// Get timeout.
116-
let timeout = *matches.get_one::<u64>("timeout").unwrap();
117-
118-
// Run program.
119-
let mut csharp_cmd = Command::new(argv[0]);
120-
// Set ld preload
121-
if let Some(ld_preload) = util::get_ld_preload(&matches) {
122-
csharp_cmd.env("LD_PRELOAD", ld_preload);
123-
}
124-
if let Some(ref file) = stdin_file {
125-
csharp_cmd.stdin(std::fs::File::open(file)?);
126-
}
127-
if argv.len() > 1 {
128-
csharp_cmd.args(&argv[1..]);
129-
}
130-
let csharp_result = util::get_output(&mut csharp_cmd, timeout, true)?;
131-
132-
let csharp_stderr = String::from_utf8_lossy(&csharp_result.stderr);
133-
134-
// Create report.
135-
let mut report = CrashReport::new();
136-
// Set executable path (for C# .dll, .csproj (dotnet) or .exe (mono) file).
137-
report.executable_path = argv.get(pos).unwrap().to_string();
138-
report.proc_cmdline = argv.join(" ");
139-
let _ = report.add_os_info();
140-
let _ = report.add_proc_environ();
141-
142-
// Get C# report.
143-
let csharp_stderr_list: Vec<String> =
144-
csharp_stderr.split('\n').map(|l| l.to_string()).collect();
145-
let re = Regex::new(r"^Unhandled [Ee]xception(?::\n|\. ).*").unwrap();
146-
if let Some(start) = csharp_stderr_list.iter().position(|x| re.is_match(x)) {
147-
let end = csharp_stderr_list[start..]
148-
.iter()
149-
.rposition(|x| !x.is_empty())
150-
.unwrap()
151-
+ 1;
152-
report.csharp_report = csharp_stderr_list[start..end].to_vec();
153-
let report_str = report.csharp_report.join("\n");
154-
report.stacktrace = CSharpStacktrace::extract_stacktrace(&report_str)?;
155-
if let Some(exception) = CSharpException::parse_exception(&report_str) {
156-
report.execution_class = exception;
157-
}
158-
} else {
159-
// Call casr-san
160-
return util::call_casr_san(&matches, &argv, "casr-csharp");
161-
}
162-
163-
let stacktrace = CSharpStacktrace::parse_stacktrace(&report.stacktrace)?;
164-
if let Ok(crash_line) = stacktrace.crash_line() {
165-
report.crashline = crash_line.to_string();
166-
if let CrashLine::Source(debug) = crash_line {
167-
if let Some(sources) = CrashReport::sources(&debug) {
168-
report.source = sources;
169-
}
170-
}
171-
}
172-
173-
if let Some(path) = matches.get_one::<String>("strip-path") {
174-
util::strip_paths(&mut report, &stacktrace, path);
175-
}
176-
177-
//Output report.
178-
util::output_report(&report, &matches, &argv)
9+
casr::stub(DynMode::new::<CSharpMode>())
17910
}

0 commit comments

Comments
 (0)