Skip to content

Commit 840145d

Browse files
authored
Add casr-lua (#231)
1 parent 3b8461f commit 840145d

File tree

25 files changed

+737
-67
lines changed

25 files changed

+737
-67
lines changed

.github/workflows/aarch64.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ jobs:
2525
install: |
2626
export CARGO_TERM_COLOR=always
2727
export CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse
28-
apt-get update && apt-get install -y gdb pip curl python3.12-dev clang llvm build-essential binutils lld
28+
apt-get update && apt-get install -y gdb pip curl python3.12-dev clang llvm build-essential binutils lld lua5.4
2929
curl https://sh.rustup.rs -o rustup.sh && chmod +x rustup.sh && \
3030
./rustup.sh -y && rm rustup.sh
3131
run: |

.github/workflows/amd64.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ jobs:
2020
- name: Run tests
2121
run: |
2222
sudo apt update && sudo apt install -y gdb pip curl python3-dev llvm \
23-
openjdk-17-jdk ca-certificates gnupg
23+
openjdk-17-jdk ca-certificates gnupg lua5.4
2424
# Atheris fails to install on Ubuntu 24.04, see https://github.com/google/atheris/issues/82
2525
# pip3 install atheris
2626
sudo mkdir -p /etc/apt/keyrings

.github/workflows/coverage.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ jobs:
1919
- name: Install Dependences
2020
run: |
2121
sudo apt update && sudo apt install -y gdb pip curl python3-dev llvm \
22-
openjdk-17-jdk ca-certificates gnupg
22+
openjdk-17-jdk ca-certificates gnupg lua5.4
2323
pip3 install atheris
2424
sudo mkdir -p /etc/apt/keyrings
2525
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg

.github/workflows/darwin-arm64.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ jobs:
2020
run: |
2121
arch -x86_64 /usr/local/bin/brew update
2222
arch -x86_64 /usr/local/bin/brew install gdb curl python llvm \
23-
openjdk ca-certificates gnupg nodejs --overwrite
23+
openjdk ca-certificates gnupg nodejs lua5.4 --overwrite
2424
- name: Build
2525
run: cargo build --all-features --verbose
2626
- name: NPM packages

.github/workflows/riscv64.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ jobs:
2525
install: |
2626
export CARGO_TERM_COLOR=always
2727
export CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse
28-
apt-get update \
29-
&& apt-get install -y gdb pip curl python3-dev clang llvm build-essential
28+
apt-get update && apt-get install -y gdb pip curl python3-dev clang llvm \
29+
build-essential lua5.4
3030
curl https://sh.rustup.rs -o rustup.sh && chmod +x rustup.sh && \
3131
./rustup.sh -y && rm rustup.sh
3232
run: |

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Cargo.lock
55
*/tests/tmp_tests_casr
66
*/tests/casr_tests/csharp/*/bin
77
*/tests/casr_tests/csharp/*/obj
8+
*.swo
89
*.swp
910
node_modules
1011
*/node_modules/*

README.md

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,12 @@ ASAN reports or `casr-ubsan` to analyze UBSAN reports. Try `casr-gdb` to get
3131
reports from gdb. Use `casr-python` to analyze python reports and get report
3232
from [Atheris](https://github.com/google/atheris). Use `casr-java` to analyze
3333
java reports and get report from
34-
[Jazzer](https://github.com/CodeIntelligenceTesting/jazzer). Use `casr-js`
35-
to analyze JavaScript reports and get report from
34+
[Jazzer](https://github.com/CodeIntelligenceTesting/jazzer). Use `casr-js` to
35+
analyze JavaScript reports and get report from
3636
[Jazzer.js](https://github.com/CodeIntelligenceTesting/jazzer.js) or
37-
[jsfuzz](https://github.com/fuzzitdev/jsfuzz).
38-
Use `casr-csharp` to analyze C# reports and get report from
39-
[Sharpfuzz](https://github.com/Metalnem/sharpfuzz).
37+
[jsfuzz](https://github.com/fuzzitdev/jsfuzz). Use `casr-csharp` to analyze C#
38+
reports and get report from [Sharpfuzz](https://github.com/Metalnem/sharpfuzz).
39+
Use `casr-lua` to analyze Lua reports.
4040

4141
Crash report contains many useful information: severity (like [exploitable](https://github.com/jfoote/exploitable))
4242
for x86, x86\_64, arm32, aarch64, rv32g, rv64g architectures,
@@ -80,12 +80,13 @@ It can analyze crashes from different sources:
8080
and program languages:
8181

8282
* C/C++
83-
* Rust
83+
* C#
8484
* Go
85-
* Python
8685
* Java
8786
* JavaScript
88-
* C#
87+
* Lua
88+
* Python
89+
* Rust
8990

9091
It could be built with `exploitable` feature for severity estimation crashes
9192
collected from gdb. To save crash reports as json use `serde` feature.
@@ -170,6 +171,10 @@ Create report from C#:
170171

171172
$ casr-csharp -o csharp.casrep -- dotnet run --project casr/tests/casr_tests/csharp/test_casr_csharp/test_casr_csharp.csproj
172173

174+
Create report from Lua:
175+
176+
$ casr-lua -o lua.casrep -- casr/tests/casr_tests/lua/test_casr_lua.lua
177+
173178
View report:
174179

175180
$ casr-cli casr/tests/casr_tests/casrep/test_clustering_san/load_fuzzer_crash-120697a7f5b87c03020f321c8526adf0f4bcc2dc.casrep

casr/src/bin/casr-cli.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,16 @@ fn build_tree_report(
429429
tree.collapse_item(row);
430430
}
431431

432+
if !report.lua_report.is_empty() {
433+
row = tree
434+
.insert_container_item("LuaReport".to_string(), Placement::After, row)
435+
.unwrap();
436+
report.lua_report.iter().for_each(|e| {
437+
tree.insert_item(e.clone(), Placement::LastChild, row);
438+
});
439+
tree.collapse_item(row);
440+
}
441+
432442
if !report.java_report.is_empty() {
433443
row = tree
434444
.insert_container_item("JavaReport".to_string(), Placement::After, row)
@@ -652,6 +662,10 @@ fn build_slider_report(
652662
select.add_item("PythonReport", report.python_report.join("\n"));
653663
}
654664

665+
if !report.lua_report.is_empty() {
666+
select.add_item("LuaReport", report.lua_report.join("\n"));
667+
}
668+
655669
if !report.java_report.is_empty() {
656670
select.add_item("JavaReport", report.java_report.join("\n"));
657671
}

casr/src/bin/casr-lua.rs

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
use casr::util;
2+
use libcasr::{
3+
init_ignored_frames,
4+
lua::LuaException,
5+
report::CrashReport,
6+
severity::Severity,
7+
stacktrace::Filter,
8+
stacktrace::Stacktrace,
9+
stacktrace::{CrashLine, CrashLineExt},
10+
};
11+
12+
use anyhow::{bail, Result};
13+
use clap::{Arg, ArgAction, ArgGroup};
14+
use std::path::{Path, PathBuf};
15+
use std::process::Command;
16+
17+
fn main() -> Result<()> {
18+
let matches = clap::Command::new("casr-lua")
19+
.version(clap::crate_version!())
20+
.about("Create CASR reports (.casrep) from Lua reports")
21+
.term_width(90)
22+
.arg(
23+
Arg::new("output")
24+
.short('o')
25+
.long("output")
26+
.action(ArgAction::Set)
27+
.value_parser(clap::value_parser!(PathBuf))
28+
.value_name("REPORT")
29+
.help(
30+
"Path to save report. Path can be a directory, then report name is generated",
31+
),
32+
)
33+
.arg(
34+
Arg::new("stdout")
35+
.action(ArgAction::SetTrue)
36+
.long("stdout")
37+
.help("Print CASR report to stdout"),
38+
)
39+
.group(
40+
ArgGroup::new("out")
41+
.args(["stdout", "output"])
42+
.required(true),
43+
)
44+
.arg(
45+
Arg::new("stdin")
46+
.long("stdin")
47+
.action(ArgAction::Set)
48+
.value_parser(clap::value_parser!(PathBuf))
49+
.value_name("FILE")
50+
.help("Stdin file for program"),
51+
)
52+
.arg(
53+
Arg::new("timeout")
54+
.short('t')
55+
.long("timeout")
56+
.action(ArgAction::Set)
57+
.default_value("0")
58+
.value_name("SECONDS")
59+
.help("Timeout (in seconds) for target execution, 0 value means that timeout is disabled")
60+
.value_parser(clap::value_parser!(u64))
61+
)
62+
.arg(
63+
Arg::new("ignore")
64+
.long("ignore")
65+
.action(ArgAction::Set)
66+
.value_parser(clap::value_parser!(PathBuf))
67+
.value_name("FILE")
68+
.help("File with regular expressions for functions and file paths that should be ignored"),
69+
)
70+
.arg(
71+
Arg::new("strip-path")
72+
.long("strip-path")
73+
.env("CASR_STRIP_PATH")
74+
.action(ArgAction::Set)
75+
.value_name("PREFIX")
76+
.help("Path prefix to strip from stacktrace"),
77+
)
78+
.arg(
79+
Arg::new("ARGS")
80+
.action(ArgAction::Set)
81+
.num_args(1..)
82+
.last(true)
83+
.required(true)
84+
.help("Add \"-- <path> <arguments>\" to run"),
85+
)
86+
.get_matches();
87+
88+
init_ignored_frames!("lua");
89+
if let Some(path) = matches.get_one::<PathBuf>("ignore") {
90+
util::add_custom_ignored_frames(path)?;
91+
}
92+
// Get program args.
93+
let argv: Vec<&str> = if let Some(argvs) = matches.get_many::<String>("ARGS") {
94+
argvs.map(|s| s.as_str()).collect()
95+
} else {
96+
bail!("Wrong arguments for starting program");
97+
};
98+
99+
// Get stdin for target program.
100+
let stdin_file = util::stdin_from_matches(&matches)?;
101+
102+
// Get timeout
103+
let timeout = *matches.get_one::<u64>("timeout").unwrap();
104+
105+
// Run program.
106+
let mut cmd = Command::new(argv[0]);
107+
if let Some(ref file) = stdin_file {
108+
cmd.stdin(std::fs::File::open(file)?);
109+
}
110+
if argv.len() > 1 {
111+
cmd.args(&argv[1..]);
112+
}
113+
let result = util::get_output(&mut cmd, timeout, true)?;
114+
let stderr = String::from_utf8_lossy(&result.stderr);
115+
116+
// Create report.
117+
let mut report = CrashReport::new();
118+
report.executable_path = argv[0].to_string();
119+
if argv.len() > 1 {
120+
if let Some(fname) = Path::new(argv[0]).file_name() {
121+
let fname = fname.to_string_lossy();
122+
if fname.starts_with("lua") && !fname.ends_with(".lua") && argv[1].ends_with(".lua") {
123+
report.executable_path = argv[1].to_string();
124+
}
125+
}
126+
}
127+
report.proc_cmdline = argv.join(" ");
128+
let _ = report.add_os_info();
129+
let _ = report.add_proc_environ();
130+
131+
// Extract lua exception
132+
let Some(exception) = LuaException::new(&stderr) else {
133+
bail!("Lua exception is not found!");
134+
};
135+
136+
// Parse exception
137+
report.lua_report = exception.lua_report();
138+
report.stacktrace = exception.extract_stacktrace()?;
139+
report.execution_class = exception.severity()?;
140+
if let Ok(crashline) = exception.crash_line() {
141+
report.crashline = crashline.to_string();
142+
if let CrashLine::Source(debug) = crashline {
143+
if let Some(sources) = CrashReport::sources(&debug) {
144+
report.source = sources;
145+
}
146+
}
147+
}
148+
let stacktrace = exception.parse_stacktrace()?;
149+
if let Some(path) = matches.get_one::<String>("strip-path") {
150+
util::strip_paths(&mut report, &stacktrace, path);
151+
}
152+
153+
//Output report
154+
util::output_report(&report, &matches, &argv)
155+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#!/bin/env lua
2+
3+
function f(a, b)
4+
a = a .. 'qwer'
5+
b = b * 123
6+
c = a / b
7+
return c
8+
end
9+
10+
function g(a)
11+
a = a .. 'qwer'
12+
b = 123
13+
c = f(a, b)
14+
return c
15+
end
16+
17+
function h()
18+
a = 'qwer'
19+
c = g(a)
20+
return c
21+
end
22+
23+
print(h())

0 commit comments

Comments
 (0)