diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml deleted file mode 100644 index 2a836946..00000000 --- a/.github/workflows/rust.yml +++ /dev/null @@ -1,104 +0,0 @@ -name: Rust - -on: - push: - branches: [v2] - pull_request: - merge_group: - types: [checks_requested] - workflow_dispatch: - -env: - CARGO_TERM_COLOR: always - -jobs: - build: - name: Build and test (${{ matrix.name }}) - runs-on: ubuntu-latest - strategy: - matrix: - include: - - name: stable - toolchain: stable - fuzz: true - - name: beta - toolchain: beta - fuzz: true - - name: nightly - toolchain: nightly - nightly: true - - name: MSRV - toolchain: "1.86.0" - - name: no_std - toolchain: stable - target: thumbv7m-none-eabi - no_std: true - steps: - - uses: actions/checkout@v7 - - - name: Install packages for fuzzing - if: matrix.fuzz - run: sudo apt-get update -y && sudo apt-get install -y binutils-dev libunwind8-dev libcurl4-openssl-dev libelf-dev libdw-dev cmake gcc libiberty-dev - - - name: Install toolchain - uses: dtolnay/rust-toolchain@master - with: - toolchain: ${{ matrix.toolchain }} - target: ${{ matrix.target }} - - - name: Style check - if: matrix.nightly - run: rustup component add rustfmt && cargo fmt --all --check - - - name: Clippy check - if: matrix.nightly - run: rustup component add clippy && cargo clippy --all-features --all-targets -- -D warnings - - - name: Build - run: cargo build --verbose - - - name: Run tests - if: ${{ !matrix.no_std }} - run: cargo test --verbose - - - name: Cargo test no default features - if: matrix.nightly - run: cargo test --verbose --no-default-features - - - name: Cargo test all features - if: matrix.nightly - run: cargo test --verbose --all-features - - - name: Cargo doc all features - if: matrix.nightly - run: cargo doc --all-features --verbose - - - name: Cargo bench no default features - if: matrix.nightly - run: cargo clean && cargo bench --verbose --no-default-features - - - name: Cargo bench all features - if: matrix.nightly - run: cargo clean && cargo bench --verbose --all-features - - - name: miri - if: matrix.nightly - run: rustup component add miri && cargo miri test --verbose --all-features - - - name: fuzz - if: matrix.fuzz - working-directory: fuzz - run: ./travis-fuzz.sh - - build_result: - name: homu build finished - runs-on: ubuntu-latest - needs: build - - steps: - - name: Mark the job as successful - run: exit 0 - if: success() - - name: Mark the job as unsuccessful - run: exit 1 - if: "!success()" diff --git a/.gitignore b/.gitignore index 858f0d52..af93bbc0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ target -/fuzz/hfuzz_target /.vscode diff --git a/Cargo.lock b/Cargo.lock index e56831b8..6ab62b8e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -43,6 +43,9 @@ name = "arbitrary" version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" +dependencies = [ + "derive_arbitrary", +] [[package]] name = "autocfg" @@ -104,6 +107,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ad534f4357a5264cce5019c989cf66a4f0dc4e0d1b1d15f8aacec0ff7360273" dependencies = [ "find-msvc-tools", + "jobserver", + "libc", "shlex", ] @@ -277,6 +282,17 @@ dependencies = [ "thiserror", ] +[[package]] +name = "derive_arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + [[package]] name = "either" version = "1.18.0" @@ -350,6 +366,17 @@ dependencies = [ "slab", ] +[[package]] +name = "getrandom" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +dependencies = [ + "cfg-if", + "libc", + "r-efi", +] + [[package]] name = "half" version = "2.7.1" @@ -392,6 +419,16 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" +[[package]] +name = "jobserver" +version = "0.1.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3" +dependencies = [ + "getrandom", + "libc", +] + [[package]] name = "js-sys" version = "0.3.104" @@ -409,6 +446,16 @@ version = "0.2.189" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" +[[package]] +name = "libfuzzer-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9fd2f41a1cba099f79a0b6b6c35656cf7c03351a7bae8ff0f28f25270f929d2" +dependencies = [ + "arbitrary", + "cc", +] + [[package]] name = "malloc_size_of" version = "0.1.1" @@ -513,6 +560,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + [[package]] name = "rayon" version = "1.12.0" @@ -658,6 +711,15 @@ dependencies = [ "serde_test", ] +[[package]] +name = "smallvec-fuzz" +version = "0.0.0" +dependencies = [ + "arbitrary", + "libfuzzer-sys", + "smallvec", +] + [[package]] name = "syn" version = "2.0.119" diff --git a/Cargo.toml b/Cargo.toml index 258897f4..c548fd91 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -77,3 +77,9 @@ harness = false [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] + +[workspace] +members = [ + ".", + "fuzz", +] diff --git a/fuzz/.gitignore b/fuzz/.gitignore new file mode 100644 index 00000000..1a45eee7 --- /dev/null +++ b/fuzz/.gitignore @@ -0,0 +1,4 @@ +target +corpus +artifacts +coverage diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index ebaf78b6..d6f417f0 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -1,26 +1,23 @@ [package] name = "smallvec-fuzz" -version = "0.1.0" -authors = ["Dawid Ciężarkiewicz "] -edition = "2021" +version = "0.0.0" +authors = ["The Servo Project Developers"] publish = false +edition = "2024" [package.metadata] cargo-fuzz = true -[features] -afl_fuzz = ["afl"] -honggfuzz_fuzz = ["honggfuzz"] - - [dependencies] -honggfuzz = { version = "0.5.47", optional = true } -afl = { version = "0.4", optional = true } -smallvec = { path = ".." } +arbitrary = { version = "1.4", features = ["derive"] } +libfuzzer-sys = "0.4" -[workspace] -members = ["."] +[dependencies.smallvec] +path = ".." [[bin]] -name = "smallvec_ops" -path = "fuzz_targets/smallvec_ops.rs" +name = "main" +path = "fuzz_targets/main.rs" +test = false +doc = false +bench = false diff --git a/fuzz/README.md b/fuzz/README.md deleted file mode 100644 index 32305709..00000000 --- a/fuzz/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# Fuzzer for smallvec - -Based on fuzzing in [rust-bitcoin](https://github.com/rust-bitcoin/rust-bitcoin/tree/c8ac25219a09bf9d017f1b05abe3e746e2136f73/fuzz) - -## Running manually with afl - -``` -cargo afl build --release --bin smallvec_ops --features afl && cargo afl fuzz -i in -o out target/release/smallvec_ops -``` - -# Useful links: -* https://rust-fuzz.github.io/book/afl.html diff --git a/fuzz/fuzz_targets/main.rs b/fuzz/fuzz_targets/main.rs new file mode 100644 index 00000000..1a8bd1e3 --- /dev/null +++ b/fuzz/fuzz_targets/main.rs @@ -0,0 +1,306 @@ +//! Simple fuzzer testing all available `SmallVec` operations + +#![no_main] + +use { + arbitrary::Arbitrary, + libfuzzer_sys::fuzz_target, + smallvec::SmallVec, + std::fmt::Debug +}; + +/// A generic wrapper that bounds data generated via `arbitrary`. +/// Default cap is 255. +#[derive(Debug, Clone)] +pub struct Bounded(pub T); + +// Bounded `usize` between `0..=CAP` +impl<'a, const CAP: usize> Arbitrary<'a> for Bounded { + #[inline] + fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { + Ok(Bounded(u.int_in_range(0..=CAP)?)) + } +} + +// Bounded `Vec` whose length is between `0..=CAP` +impl<'a, T, const CAP: usize> Arbitrary<'a> for Bounded, CAP> +where T: Arbitrary<'a> +{ + fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { + let len = u.int_in_range(0..=CAP)?; + let vec = u + .arbitrary_iter()? + .take(len) + .collect::, _>>()?; + Ok(Bounded(vec)) + } +} + +#[inline] +fn choose_range( + u: &mut arbitrary::Unstructured, + len: usize +) -> arbitrary::Result> { + let start = u.int_in_range(0..=len)?; + let end = u.int_in_range(start..=len)?; + Ok(start..end) +} + +#[derive(Arbitrary, Debug)] +enum Op { + New, + WithCapacity(Bounded), + FromVec, + FromSlice(Bounded>), + Push(usize), + Pop, + Grow(Bounded), + Reserve(Bounded), + ReserveExact(Bounded), + ShrinkToFit, + Truncate, + SwapRemove, + Clear, + Remove, + Insert(usize), + Drain, + Splice(Bounded>), + RetainEven, + Dedup, + ExtendFromSlice(Bounded>), + ExtendFromWithin, + Resize(Bounded, usize) +} + +/// Helper to assert equivalence of all structural invariants of `SmallVec` +/// against `alloc::Vec` +fn assert_invariants( + small_vec: &mut SmallVec, + std_vec: &mut Vec +) { + // Length and content equivalence + assert_eq!(small_vec.len(), std_vec.len(), "`len()` mismatch"); + assert_eq!( + small_vec.is_empty(), + std_vec.is_empty(), + "`is_empty()` mismatch" + ); + assert_eq!( + small_vec.as_slice(), + std_vec.as_slice(), + "`as_slice()` mismatch" + ); + + // Capacity & spilling invariants + assert!( + small_vec.capacity() >= small_vec.len(), + "`capacity()` is smaller than `len()`" + ); + assert!( + small_vec.capacity() >= N, + "`capacity()` is smaller than inline size `N`" + ); + assert_eq!( + small_vec.spilled(), + small_vec.capacity() > N, + "`spilled()` doesn't equal to `capacity() > N`" + ); + + // Iterator invariants + assert!( + small_vec.iter().eq(std_vec.iter()), + "iterator yield mismatch" + ); + assert!( + small_vec.iter().rev().eq(std_vec.iter().rev()), + "reverse iterator yield mismatch" + ); + assert!( + small_vec + .clone() + .into_iter() + .eq(std_vec.clone().into_iter()), + "`into_iter()` yield mismatch" + ); + assert_eq!( + small_vec.iter().size_hint(), + std_vec.iter().size_hint(), + "`size_hint()` mismatch" + ); + assert_eq!( + small_vec.iter().len(), + std_vec.iter().len(), + "`ExactSizeIterator::len()` mismatch" + ); + + // Clone invariant + assert_eq!(small_vec.clone(), *std_vec, "clone mismatch"); +} + +fn test_with_inline_cap( + u: &mut arbitrary::Unstructured, + ops: &[Op] +) -> arbitrary::Result<()> { + // We let `T` be `usize` instead of `u8` because, albeit less efficient, + // this incurs potential memory misalignment which should be properly + // handled by the library. + + let mut small_vec = SmallVec::::new(); + let mut std_vec = Vec::::new(); + + for op in ops { + match op { + Op::New => { + small_vec = SmallVec::new(); + std_vec = Vec::new(); + } + Op::WithCapacity(cap) => { + small_vec = SmallVec::with_capacity(cap.0); + std_vec = Vec::with_capacity(cap.0); + } + Op::FromVec => { + small_vec = SmallVec::from_vec(small_vec.into_vec()); + // No-op on `Vec` + } + Op::FromSlice(data) => { + small_vec = SmallVec::from(data.0.as_slice()); + std_vec = data.0.clone(); + } + Op::Push(val) => { + small_vec.push(*val); + std_vec.push(*val); + } + Op::Pop => { + assert_eq!(small_vec.pop(), std_vec.pop(), "`pop()` mismatch"); + } + Op::Grow(target) => { + small_vec.grow(target.0); + // Mimic `SmallVec::grow` on `Vec` + if target.0 > std_vec.capacity() { + let additional = target.0 - std_vec.len(); + std_vec.reserve(additional); + } + } + Op::Reserve(amount) => { + small_vec.reserve(amount.0); + std_vec.reserve(amount.0); + } + Op::ReserveExact(amount) => { + small_vec.reserve_exact(amount.0); + std_vec.reserve_exact(amount.0); + } + Op::ShrinkToFit => { + small_vec.shrink_to_fit(); + std_vec.shrink_to_fit(); + } + Op::Truncate => { + let len = u.int_in_range(0..=small_vec.len())?; + small_vec.truncate(len); + std_vec.truncate(len); + } + Op::SwapRemove => { + if !small_vec.is_empty() { + let idx = u.choose_index(small_vec.len())?; + assert_eq!( + small_vec.swap_remove(idx), + std_vec.swap_remove(idx), + "`swap_remove()` mismatch" + ); + } + } + Op::Clear => { + small_vec.clear(); + std_vec.clear(); + } + Op::Remove => { + if !small_vec.is_empty() { + let idx = u.choose_index(small_vec.len())?; + assert_eq!( + small_vec.remove(idx), + std_vec.remove(idx), + "`remove()` mismatch" + ); + } + } + Op::Insert(val) => { + let idx = u.int_in_range(0..=small_vec.len())?; + small_vec.insert(idx, *val); + std_vec.insert(idx, *val); + } + Op::Drain => { + let len = small_vec.len(); + let range = choose_range(u, len)?; + + let small_vec_drained = small_vec.drain(range.clone()); + let std_vec_drained = std_vec.drain(range); + + assert!( + small_vec_drained.eq(std_vec_drained), + "`drain()` yield mismatch" + ); + } + Op::Splice(items) => { + let len = small_vec.len(); + let range = choose_range(u, len)?; + + let small_vec_spliced = small_vec.splice(range.clone(), items.0.clone()); + let std_vec_spliced = std_vec.splice(range, items.0.clone()); + + assert!( + small_vec_spliced.eq(std_vec_spliced), + "`splice()` yield mismatch" + ); + } + Op::RetainEven => { + small_vec.retain(|e| e % 2 == 0); + std_vec.retain(|e| e % 2 == 0); + } + Op::Dedup => { + small_vec.dedup(); + std_vec.dedup(); + } + Op::ExtendFromSlice(items) => { + small_vec.extend_from_slice(&items.0); + std_vec.extend_from_slice(&items.0); + } + Op::ExtendFromWithin => { + let len = small_vec.len(); + let range = choose_range(u, len)?; + + small_vec.extend_from_within(range.clone()); + std_vec.extend_from_within(range); + } + Op::Resize(new_len, val) => { + small_vec.resize(new_len.0, *val); + std_vec.resize(new_len.0, *val); + } + } + + assert_invariants(&mut small_vec, &mut std_vec); + } + + Ok(()) +} + +fn run_test(mut u: arbitrary::Unstructured) -> arbitrary::Result<()> { + let ops = Vec::::arbitrary(&mut u)?; + let dynamic_entropy = u.take_rest(); + + let run_test = |test_func: fn(&mut arbitrary::Unstructured, &[Op]) -> arbitrary::Result<()>| { + test_func(&mut arbitrary::Unstructured::new(dynamic_entropy), &ops) + }; + + run_test(test_with_inline_cap::<0>)?; + run_test(test_with_inline_cap::<1>)?; + run_test(test_with_inline_cap::<2>)?; + run_test(test_with_inline_cap::<7>)?; + run_test(test_with_inline_cap::<8>)?; + + Ok(()) +} + +fuzz_target!(|data: &[u8]| { + let u = arbitrary::Unstructured::new(data); + + let _ = run_test(u); +}); diff --git a/fuzz/fuzz_targets/smallvec_ops.rs b/fuzz/fuzz_targets/smallvec_ops.rs deleted file mode 100644 index 991793dc..00000000 --- a/fuzz/fuzz_targets/smallvec_ops.rs +++ /dev/null @@ -1,237 +0,0 @@ -//! Simple fuzzer testing all available `SmallVec` operations -use smallvec::SmallVec; - -// There's no point growing too much, so try not to grow -// over this size. -const CAP_GROWTH: usize = 256; - -macro_rules! next_usize { - ($b:ident) => { - $b.next().unwrap_or(0) as usize - }; -} - -macro_rules! next_u8 { - ($b:ident) => { - $b.next().unwrap_or(0) - }; -} - -fn black_box_iter(i: impl Iterator) { - // print to work as a black_box - print!("{}", i.fold(0u8, |acc, e| acc.wrapping_add(e))); -} - -fn black_box_slice(s: &[u8]) { - black_box_iter(s.iter().copied()) -} - -fn black_box_mut_slice(s: &mut [u8]) { - s.iter_mut().map(|e| *e = e.wrapping_add(1)).count(); - black_box_iter((s as &[u8]).iter().copied()) -} - -fn do_test(data: &[u8]) -> SmallVec { - let mut v = SmallVec::::new(); - - let mut bytes = data.iter().copied(); - - while let Some(op) = bytes.next() { - match op % 27 { - 0 => { - v = SmallVec::new(); - } - 1 => { - v = SmallVec::with_capacity(next_usize!(bytes)); - } - 2 => { - v = SmallVec::from_vec(v.to_vec()); - } - 3 => { - black_box_iter(v.drain(..)); - } - 4 => { - if v.len() < CAP_GROWTH { - v.push(next_u8!(bytes)) - } - } - 5 => { - v.pop(); - } - 6 => v.grow(next_usize!(bytes) + v.len()), - 7 => { - if v.len() < CAP_GROWTH { - v.reserve(next_usize!(bytes)) - } - } - 8 => { - if v.len() < CAP_GROWTH { - v.reserve_exact(next_usize!(bytes)) - } - } - 9 => v.shrink_to_fit(), - 10 => v.truncate(next_usize!(bytes)), - 11 => black_box_slice(v.as_slice()), - 12 => black_box_mut_slice(v.as_mut_slice()), - 13 => { - if !v.is_empty() { - v.swap_remove(next_usize!(bytes) % v.len()); - } - } - 14 => { - v.clear(); - } - 15 => { - if !v.is_empty() { - v.remove(next_usize!(bytes) % v.len()); - } - } - 16 => { - let insert_pos = next_usize!(bytes) % (v.len() + 1); - v.insert(insert_pos, next_u8!(bytes)); - } - 17 => { - let insert_pos = next_usize!(bytes) % (v.len() + 1); - let how_many = next_usize!(bytes); - let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { - v.splice(insert_pos..insert_pos, (0..how_many).map(|_| bytes.next().unwrap())); - })); - - if result.is_err() { - assert!(bytes.next().is_none()); - } - } - 18 => { - v = SmallVec::from_vec(v.into_vec()); - } - - 19 => { - let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { - v.retain_mut(|e| { - let alt_e = bytes.next().unwrap(); - let retain = *e >= alt_e; - *e = e.wrapping_add(alt_e); - retain - }); - })); - - if result.is_err() { - assert!(bytes.next().is_none()); - } - } - 20 => { - v.dedup(); - } - - 21 => { - let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { - v.dedup_by(|a, b| { - let substitute = bytes.next().unwrap(); - let dedup = a == b; - *a = a.wrapping_add(substitute); - *b = b.wrapping_add(substitute); - dedup - }); - })); - - if result.is_err() { - assert!(bytes.next().is_none()); - } - } - 22 => { - v = SmallVec::from(data); - } - - 23 => { - if v.len() < CAP_GROWTH { - v.extend_from_slice(data) - } - } - - 24 => { - let a = next_usize!(bytes) % (v.len() + 1); - let b = next_usize!(bytes) % (v.len() + 1); - let (start, end) = (a.min(b), a.max(b)); - v.extend_from_within(start..end); - } - - 25 => { - if v.len() < CAP_GROWTH { - v.resize(next_usize!(bytes), next_u8!(bytes)); - } - } - 26 => { - v = smallvec::from_elem(next_u8!(bytes), next_usize!(bytes)); - } - _ => panic!("booo"), - } - } - v -} - -fn do_test_all(data: &[u8]) { - do_test::<0>(data); - do_test::<1>(data); - do_test::<2>(data); - do_test::<7>(data); - do_test::<8>(data); -} - -#[cfg(feature = "afl")] -fn main() { - afl::fuzz!(|data| { - // Remove the panic hook so we can actually catch panic - // See https://github.com/rust-fuzz/afl.rs/issues/150 - std::panic::set_hook(Box::new(|_| {})); - do_test_all(data); - }); -} - -#[cfg(feature = "honggfuzz")] -fn main() { - loop { - honggfuzz::fuzz!(|data| { - // Remove the panic hook so we can actually catch panic - // See https://github.com/rust-fuzz/afl.rs/issues/150 - std::panic::set_hook(Box::new(|_| {})); - do_test_all(data); - }); - } -} - -#[cfg(test)] -mod tests { - fn extend_vec_from_hex(hex: &str, out: &mut Vec) { - let mut b = 0; - for (idx, c) in hex.as_bytes().iter().enumerate() { - b <<= 4; - match *c { - b'A'..=b'F' => b |= c - b'A' + 10, - b'a'..=b'f' => b |= c - b'a' + 10, - b'0'..=b'9' => b |= c - b'0', - b'\n' => {} - b' ' => {} - _ => panic!("Bad hex"), - } - if (idx & 1) == 1 { - out.push(b); - b = 0; - } - } - } - - #[test] - fn duplicate_crash() { - let mut a = Vec::new(); - // paste the output of `xxd -p ` here and run `cargo test` - extend_vec_from_hex( - r#" - 646e21f9f910f90200f9d9f9c7030000def9000010646e2af9f910f90264 - 6e21f9f910f90200f9d9f9c7030000def90000106400f9f9d9f9c7030000 - def90000106400f9d9f9e7f1000000d9f9e7f1000000f9 - "#, - &mut a, - ); - super::do_test_all(&a); - } -} diff --git a/fuzz/in/stub b/fuzz/in/stub deleted file mode 100644 index 587be6b4..00000000 --- a/fuzz/in/stub +++ /dev/null @@ -1 +0,0 @@ -x diff --git a/fuzz/travis-fuzz.sh b/fuzz/travis-fuzz.sh deleted file mode 100755 index ff44d253..00000000 --- a/fuzz/travis-fuzz.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash -set -e -cargo install --force honggfuzz --version "^0.5.47" -for TARGET in fuzz_targets/*; do - FILENAME=$(basename $TARGET) - FILE="${FILENAME%.*}" - if [ -d hfuzz_input/$FILE ]; then - HFUZZ_INPUT_ARGS="-f hfuzz_input/$FILE/input" - fi - HFUZZ_BUILD_ARGS="--features honggfuzz_fuzz" HFUZZ_RUN_ARGS="--run_time 30 --exit_upon_crash -v $HFUZZ_INPUT_ARGS" cargo hfuzz run $FILE - - if [ -f hfuzz_workspace/$FILE/HONGGFUZZ.REPORT.TXT ]; then - cat hfuzz_workspace/$FILE/HONGGFUZZ.REPORT.TXT - for CASE in hfuzz_workspace/$FILE/SIG*; do - cat $CASE | xxd -p - done - exit 1 - fi -done