From 45cfffcba50994001c62b47ab802baf6415bfef9 Mon Sep 17 00:00:00 2001 From: 0xllx0 Date: Fri, 22 Aug 2025 22:45:06 +0000 Subject: [PATCH 1/3] tests: add `Vec` fuzz harness Adds a basic fuzzing test harness using `cargo-fuzz` to run randomized tests against the `Vec` type. --- fuzz/.gitignore | 4 ++++ fuzz/Cargo.toml | 21 +++++++++++++++++++++ fuzz/fuzz_targets/fuzz_vec.rs | 27 +++++++++++++++++++++++++++ 3 files changed, 52 insertions(+) create mode 100644 fuzz/.gitignore create mode 100644 fuzz/Cargo.toml create mode 100644 fuzz/fuzz_targets/fuzz_vec.rs diff --git a/fuzz/.gitignore b/fuzz/.gitignore new file mode 100644 index 0000000000..1a45eee776 --- /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 0000000000..4f9bcc602e --- /dev/null +++ b/fuzz/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "heapless-fuzz" +version = "0.0.0" +publish = false +edition = "2021" + +[package.metadata] +cargo-fuzz = true + +[dependencies] +libfuzzer-sys = "0.4" + +[dependencies.heapless] +path = ".." + +[[bin]] +name = "fuzz_vec" +path = "fuzz_targets/fuzz_vec.rs" +test = false +doc = false +bench = false diff --git a/fuzz/fuzz_targets/fuzz_vec.rs b/fuzz/fuzz_targets/fuzz_vec.rs new file mode 100644 index 0000000000..22f9503213 --- /dev/null +++ b/fuzz/fuzz_targets/fuzz_vec.rs @@ -0,0 +1,27 @@ +#![no_main] + +use libfuzzer_sys::fuzz_target; + +use heapless::Vec; + +fn test_vec(data: &[u8]) { + if let Ok(vec) = Vec::::from_slice(data) { + assert_eq!(vec.as_slice(), data); + } +} + +fuzz_target!(|data: &[u8]| { + match data.len() { + 0 => (), + len if len <= 16 => test_vec::<16>(data), + len if len <= 32 => test_vec::<32>(data), + len if len <= 64 => test_vec::<64>(data), + len if len <= 128 => test_vec::<128>(data), + len if len <= 256 => test_vec::<256>(data), + len if len <= 512 => test_vec::<512>(data), + len if len <= 1024 => test_vec::<1024>(data), + len if len <= 2048 => test_vec::<2048>(data), + len if len <= 4096 => test_vec::<4096>(data), + _ => (), + } +}); From 5ad0c98d0ff62511da16c4aa21578cb2689854af Mon Sep 17 00:00:00 2001 From: 0xllx0 Date: Fri, 22 Aug 2025 22:50:45 +0000 Subject: [PATCH 2/3] ci: add `testfuzz` workflow Adds the `testfuzz` workflow to CI for a short fuzz test run on each pull request. --- .github/workflows/build.yml | 38 +++++++++++++++++++++++++++++++++++++ CHANGELOG.md | 4 ++++ 2 files changed, 42 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7eede1ce58..281f1fd366 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -50,6 +50,44 @@ jobs: - name: Run miri run: MIRIFLAGS=-Zmiri-ignore-leaks cargo miri test --features="alloc,defmt,mpmc_large,portable-atomic-critical-section,serde,ufmt,bytes" + # Run cargo-fuzz tests on nightly + testfuzz: + name: testfuzz + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Cache cargo dependencies + uses: actions/cache@v3 + with: + path: | + - ~/.cargo/bin/ + - ~/.cargo/registry/index/ + - ~/.cargo/registry/cache/ + - ~/.cargo/git/db/ + key: ${{ runner.OS }}-cargo-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + ${{ runner.OS }}-cargo- + + - name: Cache build output dependencies + uses: actions/cache@v3 + with: + path: target + key: ${{ runner.OS }}-build-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + ${{ runner.OS }}-build- + + - name: Install Rust + uses: dtolnay/rust-toolchain@master + with: + toolchain: nightly + + - name: Run cargo-fuzz test + run: + - cargo install --locked cargo-fuzz + - cargo +nightly fuzz run fuzz_vec -- -runs=16384 -max_len=4096 + # Run cargo test test: name: test diff --git a/CHANGELOG.md b/CHANGELOG.md index 23e59a81fc..60199ac4ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Make MSRV of 1.87.0 explicit. +### Added + +- Added `Vec` fuzz test harness + CI runner. + ## [v0.9.1] - 2025-08-19 ### Added From b907449fbe7051b3be22a8258c7c103720adcccb Mon Sep 17 00:00:00 2001 From: 0xllx0 Date: Wed, 27 Aug 2025 15:35:16 +0000 Subject: [PATCH 3/3] fuzz: add `String` fuzz harness Adds a basic fuzzing harness for `String` UTF-8 and UTF-16 parsing. --- .github/workflows/build.yml | 1 + fuzz/Cargo.toml | 7 ++++++ fuzz/fuzz_targets/fuzz_string.rs | 41 ++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+) create mode 100644 fuzz/fuzz_targets/fuzz_string.rs diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 281f1fd366..83fbc9f6f3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -87,6 +87,7 @@ jobs: run: - cargo install --locked cargo-fuzz - cargo +nightly fuzz run fuzz_vec -- -runs=16384 -max_len=4096 + - cargo +nightly fuzz run fuzz_string -- -runs=16384 -max_len=4096 # Run cargo test test: diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 4f9bcc602e..bbb65f9cdc 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -19,3 +19,10 @@ path = "fuzz_targets/fuzz_vec.rs" test = false doc = false bench = false + +[[bin]] +name = "fuzz_string" +path = "fuzz_targets/fuzz_string.rs" +test = false +doc = false +bench = false diff --git a/fuzz/fuzz_targets/fuzz_string.rs b/fuzz/fuzz_targets/fuzz_string.rs new file mode 100644 index 0000000000..4f360922fe --- /dev/null +++ b/fuzz/fuzz_targets/fuzz_string.rs @@ -0,0 +1,41 @@ +#![no_main] + +use libfuzzer_sys::fuzz_target; + +use heapless::{String, Vec}; + +fn test_string(data: &[u8]) { + let v16_be = data + .chunks_exact(2) + .map(|c| u16::from_be_bytes([c[0], c[1]])) + .collect::>(); + + let v16_le = data + .chunks_exact(2) + .map(|c| u16::from_le_bytes([c[0], c[1]])) + .collect::>(); + + String::::from_utf16(v16_be.as_slice()).ok(); + String::::from_utf16(v16_le.as_slice()).ok(); + + Vec::::from_slice(data) + .map_err(|_| ()) + .and_then(|v| String::::from_utf8(v).map_err(|_| ())) + .ok(); +} + +fuzz_target!(|data: &[u8]| { + match data.len() { + 0 => (), + len if len <= 16 => test_string::<16>(data), + len if len <= 32 => test_string::<32>(data), + len if len <= 64 => test_string::<64>(data), + len if len <= 128 => test_string::<128>(data), + len if len <= 256 => test_string::<256>(data), + len if len <= 512 => test_string::<512>(data), + len if len <= 1024 => test_string::<1024>(data), + len if len <= 2048 => test_string::<2048>(data), + len if len <= 4096 => test_string::<4096>(data), + _ => (), + } +});