From 4aff3f495803b39002da87fa5d7583ad992ec226 Mon Sep 17 00:00:00 2001 From: Stu Small Date: Wed, 31 Jan 2024 08:29:27 -0700 Subject: [PATCH] Add fuzzing for the rust parser This adds a fuzzing task for the rust parser. This is run as release to avoid various debug assertions in the project. I've had this running on my workstation and it hasn't found anything, but it is useful to keep in CI to catch regressions. I recongize that fuzzing adds extra time to the pipeline, so I did a few things to try and minimize the impact. * First, it is a seperate, parallel workflow. Since the main build and test is a debug build and fuzz is a release build there is very little wasted work in rebuilding. * Second, I added caches for both tasks. This should help keep build time town in general. * The time the fuzzing task will run is capped at 2 minutes. This is pretty short. This was picked to not be too much longer than previous runs. This is a bit of a trade off. The longer this value is, the better the coverage. Later I will take a look at having it excercise more parser configuration options to get better coverage. At first I just wanted to try and cover the default case well. --- .github/workflows/bindings.yml | 2 +- .github/workflows/playground.yml | 2 +- .github/workflows/rust.yml | 35 +++++++++++++++++++++++++++++--- .gitignore | 1 + Cargo.lock | 25 +++++++++++++++++++++++ Cargo.toml | 2 +- fuzz/.gitignore | 4 ++++ fuzz/Cargo.toml | 21 +++++++++++++++++++ fuzz/fuzz_targets/fuzz_parser.rs | 10 +++++++++ 9 files changed, 96 insertions(+), 6 deletions(-) create mode 100644 fuzz/.gitignore create mode 100644 fuzz/Cargo.toml create mode 100644 fuzz/fuzz_targets/fuzz_parser.rs diff --git a/.github/workflows/bindings.yml b/.github/workflows/bindings.yml index aaaf5ef..9ea8965 100644 --- a/.github/workflows/bindings.yml +++ b/.github/workflows/bindings.yml @@ -20,7 +20,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Build run: cargo build --verbose - name: Run tests diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index 3f4cb97..c27a18c 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -12,7 +12,7 @@ jobs: deploy-playground: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup wasm-pack uses: jetli/wasm-pack-action@v0.4.0 diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 31000a2..83d855a 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -11,12 +11,41 @@ env: jobs: build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 + - name: Setup cache + uses: actions/cache@v4 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ runner.os }}-cargo-stable-${{ hashFiles('**/Cargo.lock') }} - name: Build run: cargo build --verbose - name: Run tests run: cargo test --verbose + + fuzz: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Setup cache + uses: actions/cache@v4 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ runner.os }}-cargo-nightly-${{ hashFiles('**/Cargo.lock') }} + - name: Use nightly toolchain + run: rustup default nightly + - name: Install cargo fuzz + run: cargo fuzz --version || cargo install --locked cargo-fuzz + - name: Fuzz parser + run: cargo fuzz run --release fuzz_parser -- -max_total_time=120 -jobs=2 diff --git a/.gitignore b/.gitignore index cbae3ad..1aedeee 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ target/ .DS_Store out/ +.idea diff --git a/Cargo.lock b/Cargo.lock index 8c3245f..381e433 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -39,6 +39,12 @@ version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" +[[package]] +name = "arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" + [[package]] name = "ariadne" version = "0.4.0" @@ -289,6 +295,14 @@ dependencies = [ "uniffi", ] +[[package]] +name = "cooklang-fuzz" +version = "0.0.0" +dependencies = [ + "cooklang", + "libfuzzer-sys", +] + [[package]] name = "cooklang-playground" version = "0.1.0" @@ -608,6 +622,17 @@ version = "0.2.152" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" +[[package]] +name = "libfuzzer-sys" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7" +dependencies = [ + "arbitrary", + "cc", + "once_cell", +] + [[package]] name = "libgit2-sys" version = "0.16.1+1.7.1" diff --git a/Cargo.toml b/Cargo.toml index d80d313..013030c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,4 +52,4 @@ name = "convert" harness = false [workspace] -members = [".", "playground", "bindings"] +members = [".", "playground", "bindings", "fuzz"] diff --git a/fuzz/.gitignore b/fuzz/.gitignore new file mode 100644 index 0000000..1a45eee --- /dev/null +++ b/fuzz/.gitignore @@ -0,0 +1,4 @@ +target +corpus +artifacts +coverage diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml new file mode 100644 index 0000000..01ba44e --- /dev/null +++ b/fuzz/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "cooklang-fuzz" +version = "0.0.0" +publish = false +edition = "2021" + +[package.metadata] +cargo-fuzz = true + +[dependencies] +libfuzzer-sys = "0.4" + +[dependencies.cooklang] +path = ".." + +[[bin]] +name = "fuzz_parser" +path = "fuzz_targets/fuzz_parser.rs" +test = false +doc = false +bench = false diff --git a/fuzz/fuzz_targets/fuzz_parser.rs b/fuzz/fuzz_targets/fuzz_parser.rs new file mode 100644 index 0000000..05c0f2e --- /dev/null +++ b/fuzz/fuzz_targets/fuzz_parser.rs @@ -0,0 +1,10 @@ +#![no_main] + +use libfuzzer_sys::fuzz_target; + +use cooklang::{CooklangParser, Extensions, Converter}; + +fuzz_target!(|contents: &str| { + let parser = CooklangParser::new(Extensions::all(), Converter::default()); + let _ = parser.parse(&contents); +});