diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 516e635..5135a9e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -298,9 +298,54 @@ jobs: name: Build WASM package with wasm-pack run: wasm-pack build --target web --no-default-features --features alloc,panic-handler + fuzz-test: + name: Fuzz Testing + needs: check-previous-run + if: needs.check-previous-run.outputs.should_skip != 'true' + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-24.04, ubuntu-24.04-arm] + fuzz-target: + - digest_crc32_autosar + - digest_crc32_bzip2 + - digest_crc32_iscsi + - digest_crc32_iso_hdlc + - digest_crc64_ecma_182 + - digest_crc64_nvme + - checksum_crc32_autosar + - checksum_crc32_bzip2 + - checksum_crc32_iscsi + - checksum_crc32_iso_hdlc + - checksum_crc64_ecma_182 + - checksum_crc64_nvme + steps: + - uses: actions/checkout@v4 # not pinning to commit hash since this is a GitHub action, which we trust + - uses: actions-rust-lang/setup-rust-toolchain@9d7e65c320fdb52dcd45ffaa68deb6c02c8754d9 # v1.12.0 + with: + toolchain: nightly + cache-key: ${{ matrix.os }}-nightly-fuzz + - name: Install cargo-fuzz + run: cargo install cargo-fuzz + - name: Run fuzzer + run: | + cargo fuzz run ${{ matrix.fuzz-target }} -- \ + -max_total_time=60 \ + -rss_limit_mb=2048 \ + -timeout=10 \ + -print_final_stats=1 + continue-on-error: true + - name: Upload artifacts on crash + if: failure() + uses: actions/upload-artifact@v4 + with: + name: fuzz-artifacts-${{ matrix.os }}-${{ matrix.fuzz-target }} + path: fuzz/artifacts/${{ matrix.fuzz-target }}/ + tests-complete: name: All tests complete - needs: [check-previous-run, test-aarch64, test-x86_64, test-x86-linux, test-x86-windows, test-software, miri-test-x86_64, test-no-std, test-wasm] + needs: [check-previous-run, test-aarch64, test-x86_64, test-x86-linux, test-x86-windows, test-software, miri-test-x86_64, test-no-std, test-wasm, fuzz-test] if: always() runs-on: ubuntu-latest steps: @@ -323,7 +368,8 @@ jobs: [ "${{ needs.test-software.result }}" == "failure" ] || \ [ "${{ needs.miri-test-x86_64.result }}" == "failure" ] || \ [ "${{ needs.test-no-std.result }}" == "failure" ] || \ - [ "${{ needs.test-wasm.result }}" == "failure" ]; then + [ "${{ needs.test-wasm.result }}" == "failure" ] || \ + [ "${{ needs.fuzz-test.result }}" == "failure" ]; then echo "✗ One or more test jobs failed" exit 1 fi diff --git a/README.md b/README.md index 00abd68..9067ebb 100644 --- a/README.md +++ b/README.md @@ -474,6 +474,12 @@ In theory, much of the "heavy lifting" has been done, so it should be possible t PRs welcome! +## Memory Safety + +Given the heavy use of hardware intrinsics, this crate uses a decent amount of `unsafe` code. + +To help ensure memory safety, this crate is validated using [Miri](https://github.com/rust-lang/miri) on `x86_64` as well as fuzz tested using [libFuzzer](https://github.com/rust-fuzz/libfuzzer) over millions of iterations. + ## References * [Catalogue of parametrised CRC algorithms](https://reveng.sourceforge.io/crc-catalogue/all.htm) diff --git a/fuzz/.gitignore b/fuzz/.gitignore new file mode 100644 index 0000000..4889b15 --- /dev/null +++ b/fuzz/.gitignore @@ -0,0 +1,4 @@ +target +corpus +artifacts +coverage \ No newline at end of file diff --git a/fuzz/Cargo.lock b/fuzz/Cargo.lock new file mode 100644 index 0000000..b0cc264 --- /dev/null +++ b/fuzz/Cargo.lock @@ -0,0 +1,184 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" + +[[package]] +name = "cc" +version = "1.2.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97463e1064cb1b1c1384ad0a0b9c8abd0988e2a91f52606c80ef14aadb63e36" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "crc" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + +[[package]] +name = "crc-fast" +version = "1.8.0" +dependencies = [ + "crc", + "digest", + "rustversion", + "spin", +] + +[[package]] +name = "crc-fast-rust-fuzz" +version = "0.1.0" +dependencies = [ + "crc-fast", + "libfuzzer-sys", +] + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "crypto-common", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", +] + +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom", + "libc", +] + +[[package]] +name = "libc" +version = "0.2.177" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" + +[[package]] +name = "libfuzzer-sys" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5037190e1f70cbeef565bd267599242926f724d3b8a9f510fd7e0b540cfa4404" +dependencies = [ + "arbitrary", + "cc", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "spin" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml new file mode 100644 index 0000000..762d6fe --- /dev/null +++ b/fuzz/Cargo.toml @@ -0,0 +1,98 @@ +[package] +name = "crc-fast-rust-fuzz" +version = "0.1.0" +publish = false +edition = "2021" + +[package.metadata] +cargo-fuzz = true + +[dependencies] +libfuzzer-sys = "0.4" + +[dependencies.crc-fast] +path = ".." + +[[bin]] +name = "digest_crc32_autosar" +path = "fuzz_targets/digest_crc32_autosar.rs" +test = false +doc = false +bench = false + +[[bin]] +name = "digest_crc32_bzip2" +path = "fuzz_targets/digest_crc32_bzip2.rs" +test = false +doc = false +bench = false + +[[bin]] +name = "digest_crc32_iscsi" +path = "fuzz_targets/digest_crc32_iscsi.rs" +test = false +doc = false +bench = false + +[[bin]] +name = "digest_crc32_iso_hdlc" +path = "fuzz_targets/digest_crc32_iso_hdlc.rs" +test = false +doc = false +bench = false + +[[bin]] +name = "digest_crc64_ecma_182" +path = "fuzz_targets/digest_crc64_ecma_182.rs" +test = false +doc = false +bench = false + +[[bin]] +name = "digest_crc64_nvme" +path = "fuzz_targets/digest_crc64_nvme.rs" +test = false +doc = false +bench = false + +[[bin]] +name = "checksum_crc32_autosar" +path = "fuzz_targets/checksum_crc32_autosar.rs" +test = false +doc = false +bench = false + +[[bin]] +name = "checksum_crc32_bzip2" +path = "fuzz_targets/checksum_crc32_bzip2.rs" +test = false +doc = false +bench = false + +[[bin]] +name = "checksum_crc32_iscsi" +path = "fuzz_targets/checksum_crc32_iscsi.rs" +test = false +doc = false +bench = false + +[[bin]] +name = "checksum_crc32_iso_hdlc" +path = "fuzz_targets/checksum_crc32_iso_hdlc.rs" +test = false +doc = false +bench = false + +[[bin]] +name = "checksum_crc64_ecma_182" +path = "fuzz_targets/checksum_crc64_ecma_182.rs" +test = false +doc = false +bench = false + +[[bin]] +name = "checksum_crc64_nvme" +path = "fuzz_targets/checksum_crc64_nvme.rs" +test = false +doc = false +bench = false \ No newline at end of file diff --git a/fuzz/fuzz_targets/checksum_crc32_autosar.rs b/fuzz/fuzz_targets/checksum_crc32_autosar.rs new file mode 100644 index 0000000..be63463 --- /dev/null +++ b/fuzz/fuzz_targets/checksum_crc32_autosar.rs @@ -0,0 +1,10 @@ +//! Fuzz target for CRC-32/AUTOSAR checksum calculation, which is a reflected variant. + +#![no_main] + +use crc_fast::{CrcAlgorithm, checksum}; +use libfuzzer_sys::fuzz_target; + +fuzz_target!(|data: &[u8]| { + checksum(CrcAlgorithm::Crc32Autosar, data); +}); diff --git a/fuzz/fuzz_targets/checksum_crc32_bzip2.rs b/fuzz/fuzz_targets/checksum_crc32_bzip2.rs new file mode 100644 index 0000000..42592fa --- /dev/null +++ b/fuzz/fuzz_targets/checksum_crc32_bzip2.rs @@ -0,0 +1,10 @@ +//! Fuzz target for CRC-32/BZIP2 checksum calculation, which is a forward variant. + +#![no_main] + +use crc_fast::{CrcAlgorithm, checksum}; +use libfuzzer_sys::fuzz_target; + +fuzz_target!(|data: &[u8]| { + checksum(CrcAlgorithm::Crc32Bzip2, data); +}); diff --git a/fuzz/fuzz_targets/checksum_crc32_iscsi.rs b/fuzz/fuzz_targets/checksum_crc32_iscsi.rs new file mode 100644 index 0000000..04a65c9 --- /dev/null +++ b/fuzz/fuzz_targets/checksum_crc32_iscsi.rs @@ -0,0 +1,11 @@ +//! Fuzz target for CRC-32/ISCSI checksum calculation, which is a reflected variant which can use +//! fusion techniques on x86 and aarch64 architectures. + +#![no_main] + +use crc_fast::{CrcAlgorithm, checksum}; +use libfuzzer_sys::fuzz_target; + +fuzz_target!(|data: &[u8]| { + checksum(CrcAlgorithm::Crc32Iscsi, data); +}); diff --git a/fuzz/fuzz_targets/checksum_crc32_iso_hdlc.rs b/fuzz/fuzz_targets/checksum_crc32_iso_hdlc.rs new file mode 100644 index 0000000..f98af0f --- /dev/null +++ b/fuzz/fuzz_targets/checksum_crc32_iso_hdlc.rs @@ -0,0 +1,11 @@ +//! Fuzz target for CRC-32/ISO-HDLC checksum calculation, which is a reflected variant which can use +//! fusion techniques on aarch64 architectures. + +#![no_main] + +use crc_fast::{CrcAlgorithm, checksum}; +use libfuzzer_sys::fuzz_target; + +fuzz_target!(|data: &[u8]| { + checksum(CrcAlgorithm::Crc32IsoHdlc, data); +}); diff --git a/fuzz/fuzz_targets/checksum_crc64_ecma_182.rs b/fuzz/fuzz_targets/checksum_crc64_ecma_182.rs new file mode 100644 index 0000000..4cc418d --- /dev/null +++ b/fuzz/fuzz_targets/checksum_crc64_ecma_182.rs @@ -0,0 +1,10 @@ +//! Fuzz target for CRC-64/ECMA-182 checksum calculation, which is a forward variant. + +#![no_main] + +use crc_fast::{CrcAlgorithm, checksum}; +use libfuzzer_sys::fuzz_target; + +fuzz_target!(|data: &[u8]| { + checksum(CrcAlgorithm::Crc64Ecma182, data); +}); diff --git a/fuzz/fuzz_targets/checksum_crc64_nvme.rs b/fuzz/fuzz_targets/checksum_crc64_nvme.rs new file mode 100644 index 0000000..74a3218 --- /dev/null +++ b/fuzz/fuzz_targets/checksum_crc64_nvme.rs @@ -0,0 +1,10 @@ +//! Fuzz target for CRC-64/NVME checksum calculation, which is a reflected variant. + +#![no_main] + +use crc_fast::{CrcAlgorithm, checksum}; +use libfuzzer_sys::fuzz_target; + +fuzz_target!(|data: &[u8]| { + checksum(CrcAlgorithm::Crc64Nvme, data); +}); diff --git a/fuzz/fuzz_targets/digest_crc32_autosar.rs b/fuzz/fuzz_targets/digest_crc32_autosar.rs new file mode 100644 index 0000000..60d110d --- /dev/null +++ b/fuzz/fuzz_targets/digest_crc32_autosar.rs @@ -0,0 +1,12 @@ +//! Fuzz target for CRC-32/AUTOSAR digest calculation, which is a reflected variant. + +#![no_main] + +use crc_fast::{CrcAlgorithm, Digest}; +use libfuzzer_sys::fuzz_target; + +fuzz_target!(|data: &[u8]| { + let mut digest = Digest::new(CrcAlgorithm::Crc32Autosar); + digest.update(data); + digest.finalize(); +}); diff --git a/fuzz/fuzz_targets/digest_crc32_bzip2.rs b/fuzz/fuzz_targets/digest_crc32_bzip2.rs new file mode 100644 index 0000000..491ad76 --- /dev/null +++ b/fuzz/fuzz_targets/digest_crc32_bzip2.rs @@ -0,0 +1,12 @@ +//! Fuzz target for CRC-32/BZIP2 digest calculation, which is a forward variant. + +#![no_main] + +use crc_fast::{CrcAlgorithm, Digest}; +use libfuzzer_sys::fuzz_target; + +fuzz_target!(|data: &[u8]| { + let mut digest = Digest::new(CrcAlgorithm::Crc32Bzip2); + digest.update(data); + digest.finalize(); +}); diff --git a/fuzz/fuzz_targets/digest_crc32_iscsi.rs b/fuzz/fuzz_targets/digest_crc32_iscsi.rs new file mode 100644 index 0000000..542a39c --- /dev/null +++ b/fuzz/fuzz_targets/digest_crc32_iscsi.rs @@ -0,0 +1,13 @@ +//! Fuzz target for CRC-32/ISCSI digest calculation, which is a reflected variant which can use +//! fusion techniques on x86 and aarch64 architectures. + +#![no_main] + +use crc_fast::{CrcAlgorithm, Digest}; +use libfuzzer_sys::fuzz_target; + +fuzz_target!(|data: &[u8]| { + let mut digest = Digest::new(CrcAlgorithm::Crc32Iscsi); + digest.update(data); + digest.finalize(); +}); diff --git a/fuzz/fuzz_targets/digest_crc32_iso_hdlc.rs b/fuzz/fuzz_targets/digest_crc32_iso_hdlc.rs new file mode 100644 index 0000000..65978d7 --- /dev/null +++ b/fuzz/fuzz_targets/digest_crc32_iso_hdlc.rs @@ -0,0 +1,13 @@ +//! Fuzz target for CRC-32/ISO-HDLC digest calculation, which is a reflected variant which can use +//! fusion techniques on aarch64 architectures. + +#![no_main] + +use crc_fast::{CrcAlgorithm, Digest}; +use libfuzzer_sys::fuzz_target; + +fuzz_target!(|data: &[u8]| { + let mut digest = Digest::new(CrcAlgorithm::Crc32IsoHdlc); + digest.update(data); + digest.finalize(); +}); diff --git a/fuzz/fuzz_targets/digest_crc64_ecma_182.rs b/fuzz/fuzz_targets/digest_crc64_ecma_182.rs new file mode 100644 index 0000000..ce737f8 --- /dev/null +++ b/fuzz/fuzz_targets/digest_crc64_ecma_182.rs @@ -0,0 +1,12 @@ +//! Fuzz target for CRC-64/ECMA-182 digest calculation, which is a forward variant. + +#![no_main] + +use crc_fast::{CrcAlgorithm, Digest}; +use libfuzzer_sys::fuzz_target; + +fuzz_target!(|data: &[u8]| { + let mut digest = Digest::new(CrcAlgorithm::Crc64Ecma182); + digest.update(data); + digest.finalize(); +}); diff --git a/fuzz/fuzz_targets/digest_crc64_nvme.rs b/fuzz/fuzz_targets/digest_crc64_nvme.rs new file mode 100644 index 0000000..54652c7 --- /dev/null +++ b/fuzz/fuzz_targets/digest_crc64_nvme.rs @@ -0,0 +1,12 @@ +//! Fuzz target for CRC-64/NVME digest calculation, which is a reflected variant. + +#![no_main] + +use crc_fast::{CrcAlgorithm, Digest}; +use libfuzzer_sys::fuzz_target; + +fuzz_target!(|data: &[u8]| { + let mut digest = Digest::new(CrcAlgorithm::Crc64Nvme); + digest.update(data); + digest.finalize(); +});