Skip to content

Commit 95ca274

Browse files
committed
fuzz: add example fuzzing targets
1 parent 82b913f commit 95ca274

File tree

9 files changed

+225
-2
lines changed

9 files changed

+225
-2
lines changed

Cargo.lock

Lines changed: 34 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[workspace]
2-
members = [ "harper-cli", "harper-core", "harper-ls", "harper-comments", "harper-wasm", "harper-tree-sitter", "harper-html", "harper-literate-haskell", "harper-typst" , "harper-stats", "harper-pos-utils", "harper-brill"]
2+
members = [ "harper-cli", "harper-core", "harper-ls", "harper-comments", "harper-wasm", "harper-tree-sitter", "harper-html", "harper-literate-haskell", "harper-typst", "fuzz" , "harper-stats", "harper-pos-utils", "harper-brill"]
33
resolver = "2"
44

55
# Comment out the below lines if you plan to use a debugger.
@@ -8,7 +8,7 @@ opt-level = 1
88

99
[profile.release]
1010
codegen-units = 1
11-
lto = true
11+
# lto = true
1212
opt-level = 3
1313
# Stripping binaries triggers a bug in `wasm-opt`.
1414
# Disable it for now.

fuzz/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
target
2+
corpus
3+
artifacts
4+
coverage

fuzz/Cargo.toml

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
[package]
2+
name = "fuzz"
3+
version = "0.0.0"
4+
publish = false
5+
edition = "2024"
6+
7+
[package.metadata]
8+
cargo-fuzz = true
9+
10+
[dependencies]
11+
libfuzzer-sys = "0.4"
12+
13+
harper-core = { path = "../harper-core" }
14+
harper-typst = { path = "../harper-typst" }
15+
harper-literate-haskell = { path = "../harper-literate-haskell" }
16+
harper-html = { path = "../harper-html" }
17+
harper-comments = { path = "../harper-comments" }
18+
19+
[[bin]]
20+
name = "fuzz_harper_typst"
21+
path = "fuzz_targets/fuzz_harper_typst.rs"
22+
test = false
23+
doc = false
24+
bench = false
25+
26+
[[bin]]
27+
name = "fuzz_harper_literate_haskell"
28+
path = "fuzz_targets/fuzz_harper_literate_haskell.rs"
29+
test = false
30+
doc = false
31+
bench = false
32+
33+
[[bin]]
34+
name = "fuzz_harper_html"
35+
path = "fuzz_targets/fuzz_harper_html.rs"
36+
test = false
37+
doc = false
38+
bench = false
39+
40+
[[bin]]
41+
name = "fuzz_harper_comment"
42+
path = "fuzz_targets/fuzz_harper_comment.rs"
43+
test = false
44+
doc = false
45+
bench = false

fuzz/README.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# cargo-fuzz targets
2+
3+
## Setup
4+
5+
Follow the rust-fuzz [setup guide](https://rust-fuzz.github.io/book/cargo-fuzz/setup.html).
6+
You need a nightly toolchain and the cargo-fuzz plugin.
7+
8+
Simple installation steps:
9+
10+
- `rustup install nightly`
11+
- `cargo install cargo-fuzz`
12+
13+
## Adding a new fuzzing target
14+
15+
To add a new target, run `cargo fuzz add $TARGET_NAME`
16+
17+
18+
## Doing a fuzzing run
19+
20+
First, make sure that lto is turned off in the workspace `Cargo.toml`, otherwise you'll encounter linker errors.
21+
If possible, prefill the `fuzz/corpus/$TARGET_NAME` directory with appropriate examples to speed up fuzzing.
22+
The fuzzer should be coverage aware, so providing a well formed input document to fuzzing targets only expecting a string as input can speed things up a lot.
23+
24+
Then, run `cargo +nightly fuzz run $TARGET_NAME -- -timeout=$TIMEOUT`
25+
26+
The timeout flag accepts a timeout in seconds, after which a long-running test case will be aborted.
27+
This should be set to a low number to quickly report endless loops / deep recursion in parsers.
28+
29+
The normal fuzzing run will continue until a crash is found.
30+
31+
## Minifying a test case
32+
33+
Once the fuzzer finds a crash, we probably want to minify the result.
34+
This can be done with `cargo +nightly fuzz tmin $TARGET $TEST_CASE_PATH`.
35+
36+
37+
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#![no_main]
2+
3+
use harper_core::parsers::{MarkdownOptions, StrParser};
4+
use libfuzzer_sys::arbitrary::{Arbitrary, Result, Unstructured};
5+
use libfuzzer_sys::fuzz_target;
6+
7+
#[derive(Debug)]
8+
struct Language(String);
9+
10+
const LANGUAGES: [&str; 32] = [
11+
"cmake",
12+
"cpp",
13+
"csharp",
14+
"c",
15+
"dart",
16+
"go",
17+
"haskell",
18+
"javascriptreact",
19+
"javascript",
20+
"java",
21+
"kotlin",
22+
"lua",
23+
"nix",
24+
"php",
25+
"python",
26+
"ruby",
27+
"rust",
28+
"scala",
29+
"shellscript",
30+
"solidity",
31+
"swift",
32+
"toml",
33+
"typescriptreact",
34+
"typescript",
35+
"clojure",
36+
"go",
37+
"lua",
38+
"java",
39+
"javascriptreact",
40+
"typescript",
41+
"typescriptreact",
42+
"solidity",
43+
];
44+
45+
impl<'a> Arbitrary<'a> for Language {
46+
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
47+
let &lang = u.choose(&LANGUAGES)?;
48+
Ok(Language(lang.to_owned()))
49+
}
50+
}
51+
52+
#[derive(Debug)]
53+
struct Input {
54+
language: Language,
55+
text: String,
56+
}
57+
58+
impl<'a> Arbitrary<'a> for Input {
59+
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
60+
let (language, text) = Arbitrary::arbitrary(u)?;
61+
Ok(Input { language, text })
62+
}
63+
64+
fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
65+
let (language, text) = Arbitrary::arbitrary_take_rest(u)?;
66+
Ok(Input { language, text })
67+
}
68+
}
69+
70+
fuzz_target!(|data: Input| {
71+
let opts = MarkdownOptions::default();
72+
let parser = harper_comments::CommentParser::new_from_language_id(&data.language.0, opts);
73+
if let Some(parser) = parser {
74+
let _res = parser.parse_str(&data.text);
75+
}
76+
});
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#![no_main]
2+
3+
use harper_core::parsers::StrParser;
4+
use libfuzzer_sys::fuzz_target;
5+
6+
fuzz_target!(|data: &str| {
7+
let parser = harper_html::HtmlParser::default();
8+
let _res = parser.parse_str(data);
9+
});
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#![no_main]
2+
3+
// use harper_core::parsers::StrParser;
4+
use libfuzzer_sys::fuzz_target;
5+
6+
fuzz_target!(|_data: &str| {
7+
// TODO: figure out how to create a literate haskell parser
8+
// let _res = typst.parse_str(&data);
9+
});
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#![no_main]
2+
3+
use harper_core::parsers::StrParser;
4+
use libfuzzer_sys::fuzz_target;
5+
6+
fuzz_target!(|data: &str| {
7+
let typst = harper_typst::Typst;
8+
let _res = typst.parse_str(data);
9+
});

0 commit comments

Comments
 (0)