Skip to content

Commit

Permalink
Merge #97
Browse files Browse the repository at this point in the history
97: Add fuzzing r=matklad a=matklad

Results are mixed: fuzzer found a manually injected `if text.starts_with('}') { panic!() }` failure, as well as "nobody needs indent larger than 80" implementation limitation. However, fuzzer wasn't able to replicate #93 :-(

If we want to run this on CI, we should add something like *somewhere*, but I am a little confused about our CI infra. I think I can add this to the end of `ci.sh`, but, ideally, this should run as a parallel CI job

```
rustup run nightly -- cargo fuzz run fmt -- -max_total_time=300
```

closes #88

Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
  • Loading branch information
bors[bot] and matklad committed Jul 31, 2019
2 parents b2e7c1a + 3d39e83 commit b758534
Show file tree
Hide file tree
Showing 10 changed files with 283 additions and 5 deletions.
1 change: 0 additions & 1 deletion Cargo.toml
Expand Up @@ -19,4 +19,3 @@ rnix = { git = "https://gitlab.com/jD91mZM2/rnix.git" }

[dev-dependencies]
unindent = "0.1.3"

15 changes: 15 additions & 0 deletions README.md
Expand Up @@ -56,6 +56,21 @@ Install Rust and Cargo or run `nix-shell` to load the project dependencies.

Then use `cargo run` to build and run the software.

### Running Fuzzer

```
$ cargo install cargo-fuzz
$ mkdir -p ./fuzz/corpus/fmt
$ cp test_data/**.nix ./fuzz/corpus/fmt
$ rustup run nightly -- cargo fuzz run fmt
```

* `fmt` is the name of the target in `./fuzz/Cargo.toml`

Fuzzer will run indefinitelly or until it finds a crash.
The crashing input is written to `fuzz/artifacts` directory.
Commit this `crash-` file, and it will be automatically tested by a unit-test.

## Documentation

* [HOWTO write new rules](docs/howto_rules.md)
Expand Down
3 changes: 3 additions & 0 deletions fuzz/.gitignore
@@ -0,0 +1,3 @@
Cargo.lock
target
corpus
208 changes: 208 additions & 0 deletions fuzz/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 21 additions & 0 deletions fuzz/Cargo.toml
@@ -0,0 +1,21 @@
[package]
name = "nixpkgs-fmt-fuzz"
version = "0.0.1"
authors = []
publish = false
edition = "2018"

[package.metadata]
cargo-fuzz = true

[dependencies]
nixpkgs-fmt = { path = "../" }
libfuzzer-sys = { git = "https://github.com/rust-fuzz/libfuzzer-sys.git" }

# Prevent this from interfering with workspaces
[workspace]
members = ["."]

[[bin]]
name = "fmt"
path = "fuzz_targets/fmt.rs"
Binary file not shown.
Binary file not shown.
10 changes: 10 additions & 0 deletions fuzz/fuzz_targets/fmt.rs
@@ -0,0 +1,10 @@
#![no_main]

#[macro_use]
extern crate libfuzzer_sys;

fuzz_target!(|data: &[u8]| {
if let Ok(text) = std::str::from_utf8(data) {
nixpkgs_fmt::reformat_string(text);
}
});
12 changes: 9 additions & 3 deletions src/engine/indentation.rs
@@ -1,6 +1,6 @@
use std::cmp::{Ord, Ordering, PartialOrd};

use rnix::{SyntaxKind::NODE_ROOT, SyntaxElement, SyntaxNode, TextUnit};
use rnix::{SyntaxElement, SyntaxKind::NODE_ROOT, SyntaxNode, TextUnit};

use crate::{
dsl::{IndentRule, Modality},
Expand Down Expand Up @@ -87,8 +87,14 @@ impl IndentLevel {
" ";
let len = self.len_as_spaces();
let len = len as usize;
assert!(len <= SPACES.len(), "don't support indent this large");
&SPACES[..len]
if len <= SPACES.len() {
&SPACES[..len]
} else {
// Someone (most likely a fuzzer) asks for gigantic indent:
// let's just leak a string
let s = std::iter::repeat(" ").take(len).collect::<String>();
&*Box::leak(Box::new(s.into_boxed_str()))
}
}

fn len_as_spaces(&self) -> u32 {
Expand Down
18 changes: 17 additions & 1 deletion src/rules.rs
Expand Up @@ -549,12 +549,28 @@ foo = x:
fn test_syntax_errors_tests() {
let test_data = {
let dir = env!("CARGO_MANIFEST_DIR");
PathBuf::from(dir).join("test_data").join("syntax_errors")
PathBuf::from(dir).join("test_data/syntax_errors")
};
let tests = TestCase::collect_from_dir(&test_data);
run(&tests);
}

#[test]
fn test_fuzz_failures() {
let failures_dir = {
let dir = env!("CARGO_MANIFEST_DIR");
PathBuf::from(dir).join("fuzz/artifacts/fmt")
};
for entry in fs::read_dir(failures_dir).unwrap() {
let entry = entry.unwrap();
if !entry.file_type().unwrap().is_file() {
continue;
}
let text = fs::read_to_string(entry.path()).unwrap();
let _ = crate::reformat_string(&text);
}
}

#[derive(Debug)]
struct TestCase {
name: Option<String>,
Expand Down

0 comments on commit b758534

Please sign in to comment.