Skip to content
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

Casr-js #176

Merged
merged 23 commits into from
Dec 12, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion .github/workflows/amd64.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,15 @@ jobs:
- name: Run tests
run: |
sudo apt update && sudo apt install -y gdb pip curl python3.10-dev llvm \
openjdk-17-jdk
openjdk-17-jdk ca-certificates gnupg
pip3 install atheris
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
export NODE_MAJOR=20
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list
sudo apt update && sudo apt install -y nodejs
sudo npm install -g jsfuzz
sudo npm install --save-dev @jazzer.js/core
curl https://sh.rustup.rs -o rustup.sh && chmod +x rustup.sh && \
./rustup.sh -y && rm rustup.sh
rustup install nightly
Expand Down
9 changes: 8 additions & 1 deletion .github/workflows/coverage.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,15 @@ jobs:
- name: Install Dependences
run: |
sudo apt update && sudo apt install -y gdb pip curl python3.10-dev llvm \
openjdk-17-jdk
openjdk-17-jdk ca-certificates gnupg
pip3 install atheris
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
export NODE_MAJOR=20
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list
sudo apt update && sudo apt install -y nodejs
sudo npm install -g jsfuzz
sudo npm install --save-dev @jazzer.js/core
curl https://sh.rustup.rs -o rustup.sh && chmod +x rustup.sh && \
./rustup.sh -y && rm rustup.sh
rustup install nightly
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@ Cargo.lock
*/Cargo.lock
*/tests/tmp_tests_casr
*.swp
node_modules
*/node_modules/*
.vscode
14 changes: 14 additions & 0 deletions casr/src/bin/casr-cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,16 @@ fn build_tree_report(
tree.collapse_item(row);
}

if !report.js_report.is_empty() {
Avgor46 marked this conversation as resolved.
Show resolved Hide resolved
row = tree
.insert_container_item("JsReport".to_string(), Placement::After, row)
.unwrap();
report.js_report.iter().for_each(|e| {
tree.insert_item(e.clone(), Placement::LastChild, row);
});
tree.collapse_item(row);
}

if !report.source.is_empty() {
row = tree
.insert_container_item("Source".to_string(), Placement::After, row)
Expand Down Expand Up @@ -629,6 +639,10 @@ fn build_slider_report(
select.add_item("RustReport", report.rust_report.join("\n"));
}

if !report.js_report.is_empty() {
select.add_item("JsReport", report.js_report.join("\n"));
}

if !report.source.is_empty() {
select.add_item("Source", report.source.join("\n"));
}
Expand Down
3 changes: 1 addition & 2 deletions casr/src/bin/casr-cluster.rs
Original file line number Diff line number Diff line change
Expand Up @@ -429,8 +429,7 @@ fn main() -> Result<()> {
.value_parser(clap::value_parser!(u32).range(1..))
)
.get_matches();

init_ignored_frames!("cpp", "rust", "python", "go", "java");
init_ignored_frames!("cpp", "rust", "python", "go", "java", "js");

// Get number of threads
let jobs = if let Some(jobs) = matches.get_one::<u32>("jobs") {
Expand Down
166 changes: 166 additions & 0 deletions casr/src/bin/casr-js.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
use casr::util;
use libcasr::{
exception::Exception, init_ignored_frames, js::*, report::CrashReport, stacktrace::*,
};

use anyhow::{bail, Result};
use clap::{Arg, ArgAction, ArgGroup};
use regex::Regex;
use std::path::{Path, PathBuf};
use std::process::Command;

fn main() -> Result<()> {
let matches = clap::Command::new("casr-js")
.version(clap::crate_version!())
.about("Create CASR reports (.casrep) from JS reports")
anfedotoff marked this conversation as resolved.
Show resolved Hide resolved
.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"),
)
.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"),
)
.arg(
Arg::new("ARGS")
.action(ArgAction::Set)
.num_args(1..)
.last(true)
.help("Add \"-- <path> <arguments>\" to run"),
)
.get_matches();

init_ignored_frames!("js"); //TODO
anfedotoff marked this conversation as resolved.
Show resolved Hide resolved
if let Some(path) = matches.get_one::<PathBuf>("ignore") {
util::add_custom_ignored_frames(path)?;
}
// 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");
};

// Get stdin for target program.
let stdin_file = util::stdin_from_matches(&matches)?;

// Get timeout
let timeout = *matches.get_one::<u64>("timeout").unwrap();

// Run program.
let mut js_cmd = Command::new(argv[0]);
if let Some(ref file) = stdin_file {
js_cmd.stdin(std::fs::File::open(file)?);
}
if argv.len() > 1 {
js_cmd.args(&argv[1..]);
}
let js_result = util::get_output(&mut js_cmd, timeout, true)?;

let js_stderr = String::from_utf8_lossy(&js_result.stderr);

// Create report.
let mut report = CrashReport::new();
// Set executable path.
report.executable_path = argv[0].to_string();
let mut path_to_tool = PathBuf::new();
path_to_tool.push(argv[0]);
if argv.len() > 1 {
if let Some(fname) = Path::new(argv[0]).file_name() {
SweetVishnya marked this conversation as resolved.
Show resolved Hide resolved
let Ok(full_path_to_tool) = which::which(fname) else {
bail!("Could not get the full path of {}", argv[0]);
};
path_to_tool = full_path_to_tool;
let fname = fname.to_string_lossy();
if (fname.ends_with("node") || fname.ends_with("jsfuzz"))
&& !fname.ends_with(".js")
Avgor46 marked this conversation as resolved.
Show resolved Hide resolved
&& argv[1].ends_with(".js")
{
report.executable_path = argv[1].to_string();
} else if argv.len() > 2
&& fname.ends_with("npx")
anfedotoff marked this conversation as resolved.
Show resolved Hide resolved
&& !fname.ends_with(".js")
Avgor46 marked this conversation as resolved.
Show resolved Hide resolved
&& argv[1] == "jazzer"
&& argv[2].ends_with(".js")
{
report.executable_path = argv[2].to_string();
}
}
}
report.proc_cmdline = argv.join(" ");
let _ = report.add_os_info();
let _ = report.add_proc_environ();

// Get JS report.
let js_stderr_list: Vec<String> = js_stderr.split('\n').map(|l| l.to_string()).collect();
let re = Regex::new(r"^(?:.*Error:(?:\s+.*)?|Thrown at:)$").unwrap();
if let Some(start) = js_stderr_list.iter().position(|x| re.is_match(x)) {
anfedotoff marked this conversation as resolved.
Show resolved Hide resolved
report.js_report = js_stderr_list[start..].to_vec();
report
.js_report
.retain(|x| !x.is_empty() && (x.trim().starts_with("at") || x.contains("Error")));
let report_str = report.js_report.join("\n");
report.stacktrace = JsStacktrace::extract_stacktrace(&report_str)?;
if let Some(exception) = JsException::parse_exception(&report.js_report[0]) {
report.execution_class = exception;
}
} else {
// Call casr-san with absolute path to interpreter/fuzzer
let mut modified_argv = argv.clone();
modified_argv[0] = path_to_tool.to_str().unwrap_or(argv[0]);
return util::call_casr_san(&matches, &modified_argv, "casr-js");
}

if let Ok(crash_line) = JsStacktrace::parse_stacktrace(&report.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;
}
}
}

//Output report
util::output_report(&report, &matches, &argv)
}
7 changes: 6 additions & 1 deletion casr/src/bin/casr-libfuzzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use std::path::{Path, PathBuf};
fn main() -> Result<()> {
let matches = clap::Command::new("casr-libfuzzer")
.version(clap::crate_version!())
.about("Triage crashes found by libFuzzer based fuzzer (C/C++/go-fuzz/Atheris/Jazzer)")
.about("Triage crashes found by libFuzzer based fuzzer (C/C++/go-fuzz/Atheris/Jazzer/Jazzer.js/jsfuzz)")
.term_width(90)
.arg(
Arg::new("log-level")
Expand Down Expand Up @@ -128,6 +128,11 @@ fn main() -> Result<()> {
"casr-python"
} else if argv[0].ends_with("jazzer") || argv[0].ends_with("java") {
"casr-java"
} else if argv[0].ends_with("node")
|| argv.len() > 1 && argv[0].ends_with("npx") && argv[1] == "jazzer"
SweetVishnya marked this conversation as resolved.
Show resolved Hide resolved
|| argv[0].ends_with("jsfuzz")
{
"casr-js"
} else {
let sym_list = util::symbols_list(Path::new(argv[0]))?;
if sym_list.contains("__asan") || sym_list.contains("runtime.go") {
Expand Down
12 changes: 12 additions & 0 deletions casr/tests/casr_tests/js/binding.gyp
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"targets": [
{
"cflags": [ "-fexceptions -fsanitize=address,fuzzer-no-link -O0 -g -fPIC" ],
"cflags_cc": [ "-fexceptions -fsanitize=address,fuzzer-no-link -O0 -g -fPIC" ],
"include_dirs" : ["<!@(node -p \"require('node-addon-api').include\")"],
"target_name": "native",
"sources": [ "native.cpp" ],
'defines': [ 'NAPI_CPP_EXCEPTIONS' ]
}
]
}
1 change: 1 addition & 0 deletions casr/tests/casr_tests/js/crash
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Avgor46 marked this conversation as resolved.
Show resolved Hide resolved
20 changes: 20 additions & 0 deletions casr/tests/casr_tests/js/native.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#include <napi.h>
#include <stdio.h>

void foo(const Napi::CallbackInfo &info)
{
Napi::Env env = info.Env();
uint8_t buf[] = {1, 2, 3};
Napi::Buffer<uint8_t> arr = Napi::Buffer<uint8_t>::New(env, &buf[0], 3);
arr[5u] = 1;
printf("Number: %u\n", arr[5u]);
// throw Napi::String::New(env, "error in native lib");
}

Napi::Object init(Napi::Env env, Napi::Object exports)
{
exports.Set(Napi::String::New(env, "foo"), Napi::Function::New(env, foo));
return exports;
};

NODE_API_MODULE(native, init);
15 changes: 15 additions & 0 deletions casr/tests/casr_tests/js/test_casr_js.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
function bar() {
new Function(`
throw new Error('internal');
`)();
}

function foo() {
bar();
}

function main() {
foo();
}

main()
18 changes: 18 additions & 0 deletions casr/tests/casr_tests/js/test_casr_js_jazzer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
function bar() {
new Function(`
throw new Error('internal');
`)();
}

function foo() {
bar();
}

function fuzz(data) {
foo();
}

module.exports.fuzz = function (data /*: Buffer */) {
const fuzzerData = data.toString();
fuzz(fuzzerData);
};
anfedotoff marked this conversation as resolved.
Show resolved Hide resolved
19 changes: 19 additions & 0 deletions casr/tests/casr_tests/js/test_casr_js_jsfuzz.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
function bar() {
new Function(`
throw new Error('internal');
`)();
}

function foo() {
bar();
}

function fuzz(data) {
foo();
}

module.exports = {
fuzz
};

fuzz(process.argv[1]);
5 changes: 5 additions & 0 deletions casr/tests/casr_tests/js/test_casr_js_native.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env node

const native_lib = require('bindings')('native')

native_lib.foo();
10 changes: 10 additions & 0 deletions casr/tests/casr_tests/js/test_casr_js_native_jazzer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const native_lib = require('bindings')('native')

function fuzz(data) {
native_lib.foo();
}

module.exports.fuzz = function (data /*: Buffer */) {
const fuzzerData = data.toString();
fuzz(fuzzerData);
};
11 changes: 11 additions & 0 deletions casr/tests/casr_tests/js/test_casr_js_native_jsfuzz.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const native_lib = require('bindings')('native')

function fuzz(data) {
native_lib.foo();
}

module.exports = {
fuzz
};

fuzz(process.argv[1]);
Loading
Loading