-
Notifications
You must be signed in to change notification settings - Fork 29
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add casr-msan
#248
Closed
Closed
Add casr-msan
#248
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,290 @@ | ||
use casr::util; | ||
use libcasr::{ | ||
msan::{MsanStacktrace, MsanContext}, | ||
constants::{ | ||
SIGINFO_SIGABRT, SIGINFO_SIGBUS, SIGINFO_SIGILL, SIGINFO_SIGSEGV, SIGINFO_SIGSYS, | ||
SIGINFO_SIGTRAP, | ||
}, | ||
cpp::CppException, | ||
exception::Exception, | ||
execution_class::*, | ||
gdb::*, | ||
init_ignored_frames, | ||
report::CrashReport, | ||
severity::Severity, | ||
stacktrace::*, | ||
}; | ||
|
||
use anyhow::{bail, Context, Result}; | ||
use clap::{Arg, ArgAction, ArgGroup}; | ||
use gdb_command::mappings::{MappedFiles, MappedFilesExt}; | ||
use gdb_command::stacktrace::StacktraceExt; | ||
use gdb_command::*; | ||
use regex::Regex; | ||
|
||
use std::env; | ||
use std::os::unix::process::{CommandExt, ExitStatusExt}; | ||
use std::path::PathBuf; | ||
use std::process::Command; | ||
|
||
fn main() -> Result<()> { | ||
let matches = clap::Command::new("casr-msan") | ||
.version(clap::crate_version!()) | ||
.about("Create CASR reports (.casrep) from MemorySanitizer reports") | ||
.term_width(90) | ||
.arg( | ||
Arg::new("output") | ||
.short('o') | ||
.long("output") | ||
.action(ArgAction::Set) | ||
.value_name("REPORT") | ||
.value_parser(clap::value_parser!(PathBuf)) | ||
.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_name("FILE") | ||
.value_parser(clap::value_parser!(PathBuf)) | ||
.help("Stdin file for program"), | ||
) | ||
.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)) | ||
) | ||
.arg( | ||
Arg::new("ignore") | ||
.long("ignore") | ||
.action(ArgAction::Set) | ||
.value_name("FILE") | ||
.value_parser(clap::value_parser!(PathBuf)) | ||
.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) | ||
.num_args(1..) | ||
.last(true) | ||
.required(true) | ||
.help("Add \"-- ./binary <arguments>\" to run executable"), | ||
) | ||
.get_matches(); | ||
|
||
// Get program args. | ||
let argv: Vec<&str> = if let Some(argvs) = matches.get_many::<String>("ARGS") { | ||
argvs.map(|s| s.as_str()).collect() | ||
} else { | ||
bail!("Wrong arguments for starting program"); | ||
}; | ||
|
||
init_ignored_frames!("cpp"); | ||
|
||
if let Some(path) = matches.get_one::<PathBuf>("ignore") { | ||
util::add_custom_ignored_frames(path)?; | ||
} | ||
// Get stdin for target program. | ||
let stdin_file = util::stdin_from_matches(&matches)?; | ||
|
||
// Get timeout | ||
let timeout = *matches.get_one::<u64>("timeout").unwrap(); | ||
|
||
// Set rss limit. | ||
if let Ok(msan_options_str) = env::var("MSAN_OPTIONS") { | ||
let mut msan_options = msan_options_str.clone(); | ||
if !msan_options_str.contains("hard_rss_limit_mb") { | ||
msan_options = [msan_options.as_str(), "hard_rss_limit_mb=2048"].join(","); | ||
} | ||
if msan_options.starts_with(',') { | ||
msan_options.remove(0); | ||
} | ||
msan_options = msan_options.replace("symbolize=0", "symbolize=1"); | ||
unsafe { | ||
std::env::set_var("MSAN_OPTIONS", msan_options); | ||
} | ||
} else { | ||
unsafe { | ||
std::env::set_var("MSAN_OPTIONS", "hard_rss_limit_mb=2048"); | ||
} | ||
} | ||
|
||
// Run program with sanitizers. | ||
let mut sanitizers_cmd = Command::new(argv[0]); | ||
if let Some(ref file) = stdin_file { | ||
sanitizers_cmd.stdin(std::fs::File::open(file).unwrap()); | ||
} | ||
if argv.len() > 1 { | ||
sanitizers_cmd.args(&argv[1..]); | ||
} | ||
#[cfg(target_os = "macos")] | ||
{ | ||
sanitizers_cmd.env("DYLD_NO_PIE", "1"); | ||
} | ||
#[cfg(target_os = "linux")] | ||
{ | ||
use linux_personality::{Personality, personality}; | ||
|
||
unsafe { | ||
sanitizers_cmd.pre_exec(|| { | ||
if personality(Personality::ADDR_NO_RANDOMIZE).is_err() { | ||
panic!("Cannot set personality"); | ||
} | ||
Ok(()) | ||
}) | ||
}; | ||
} | ||
let sanitizers_result = util::get_output(&mut sanitizers_cmd, timeout, true)?; | ||
let sanitizers_stderr = String::from_utf8_lossy(&sanitizers_result.stderr); | ||
|
||
if sanitizers_stderr.contains("Cannot set personality") { | ||
bail!("Cannot set personality (if you are running docker, allow personality syscall in your seccomp profile)"); | ||
} | ||
|
||
// Create report. | ||
let mut report = CrashReport::new(); | ||
report.executable_path = argv[0].to_string(); | ||
report.proc_cmdline = argv.join(" "); | ||
let _ = report.add_os_info(); | ||
let _ = report.add_proc_environ(); | ||
if let Some(mut file_path) = stdin_file.clone() { | ||
file_path = file_path.canonicalize().unwrap_or(file_path); | ||
report.stdin = file_path.display().to_string(); | ||
} | ||
|
||
let stacktrace: Stacktrace; | ||
|
||
// Get MASAN report. | ||
let msan_stderr_list: Vec<String> = sanitizers_stderr | ||
.split('\n') | ||
.map(|l| l.trim_end().to_string()) | ||
.collect(); | ||
let rmsan_start = | ||
Regex::new(r"==\d+==\s*WARNING: MemorySanitizer:").unwrap(); | ||
if let Some(report_start) = msan_stderr_list | ||
.iter() | ||
.position(|line| rmsan_start.is_match(line)) | ||
{ | ||
// Set MASAN report in casr report. | ||
let report_end = msan_stderr_list.iter().rposition(|s| !s.is_empty()).unwrap() + 1; | ||
report.msan_report = Vec::from(&msan_stderr_list[report_start..report_end]); | ||
let context = MsanContext(report.msan_report.clone()); | ||
let severity = context.severity(); | ||
if let Ok(severity) = severity { | ||
report.execution_class = severity; | ||
} else { | ||
eprintln!("Couldn't estimate severity. {}", severity.err().unwrap()); | ||
} | ||
report.stacktrace = MsanStacktrace::extract_stacktrace(&report.msan_report.join("\n"))?; | ||
} else { | ||
// Get termination signal. | ||
if let Some(signal) = sanitizers_result.status.signal() { | ||
// Get stack trace and mappings from gdb. | ||
match signal as u32 { | ||
SIGINFO_SIGILL | SIGINFO_SIGSYS => { | ||
report.execution_class = ExecutionClass::find("BadInstruction").unwrap(); | ||
} | ||
SIGINFO_SIGTRAP => { | ||
report.execution_class = ExecutionClass::find("TrapSignal").unwrap(); | ||
} | ||
SIGINFO_SIGABRT => { | ||
report.execution_class = ExecutionClass::find("AbortSignal").unwrap(); | ||
} | ||
SIGINFO_SIGBUS | SIGINFO_SIGSEGV => { | ||
eprintln!("Segmentation fault occurred, but there is not enough information available to determine \ | ||
exploitability. Try using casr-gdb instead."); | ||
report.execution_class = ExecutionClass::find("AccessViolation").unwrap(); | ||
} | ||
_ => { | ||
// "Undefined" is by default in report. | ||
} | ||
} | ||
|
||
// Get stack trace and mappings from gdb. | ||
let gdb_result = GdbCommand::new(&ExecType::Local(&argv)) | ||
.timeout(timeout) | ||
.stdin(&stdin_file) | ||
.r() | ||
.bt() | ||
.mappings() | ||
.launch() | ||
.with_context(|| "Unable to get results from gdb")?; | ||
|
||
let frame = Regex::new(r"^ *#[0-9]+").unwrap(); | ||
report.stacktrace = gdb_result[0] | ||
.split('\n') | ||
.filter(|x| frame.is_match(x)) | ||
.map(|x| x.to_string()) | ||
.collect::<Vec<String>>(); | ||
report.proc_maps = gdb_result[1] | ||
.split('\n') | ||
.skip(4) | ||
.map(|x| x.to_string()) | ||
.collect::<Vec<String>>(); | ||
} else { | ||
// Normal termination. | ||
bail!("Program terminated (no crash)"); | ||
} | ||
} | ||
|
||
// Get stacktrace to find crash line. | ||
stacktrace = if !report.msan_report.is_empty() { | ||
MsanStacktrace::parse_stacktrace(&report.stacktrace)? | ||
} else { | ||
let mut parsed_stacktrace = GdbStacktrace::parse_stacktrace(&report.stacktrace)?; | ||
if let Ok(mfiles) = MappedFiles::from_gdb(report.proc_maps.join("\n")) { | ||
parsed_stacktrace.compute_module_offsets(&mfiles); | ||
} | ||
parsed_stacktrace | ||
}; | ||
|
||
// Check for exceptions | ||
if let Some(class) = [CppException::parse_exception] | ||
.iter() | ||
.find_map(|parse| parse(&sanitizers_stderr)) | ||
{ | ||
report.execution_class = class; | ||
} | ||
|
||
// Get crash line. | ||
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) { | ||
report.source = sources; | ||
} | ||
} | ||
} | ||
|
||
if let Some(path) = matches.get_one::<String>("strip-path") { | ||
util::strip_paths(&mut report, &stacktrace, path); | ||
} | ||
|
||
util::output_report(&report, &matches, &argv) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
#include <stdio.h> | ||
|
||
void set_val(bool &b, const int val) { | ||
if (val > 1) { | ||
b = false; | ||
} | ||
} | ||
|
||
int main(const int argc, const char *[]) { | ||
bool b; | ||
set_val(b, argc); | ||
if (b) { | ||
printf("value set\n"); | ||
} | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The file looks like mostly a copy-paste of
casr-san.rs
. We may just addMemorySanitizer
support tocasr-san
, as it does not state any exact sanitizer in its name.