From 92ed100859a9c55d580a34c099a3f8a94375a542 Mon Sep 17 00:00:00 2001 From: Kadir Can Yildirim <252162627+kadircanyildirm-crypto@users.noreply.github.com> Date: Thu, 3 Sep 2026 23:41:36 +0300 Subject: [PATCH] Add a CI fuzz job covering x86_64 and i686 --- .github/workflows/ci.yml | 85 ++++++++++++++++++++++++++++++++++++++++ Makefile | 18 ++++++--- 2 files changed, 98 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9634a3d..87ee217 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -120,6 +120,91 @@ jobs: if: matrix.profile == 'release' run: cargo test -p spacewasm --release --target i686-unknown-linux-gnu --verbose + fuzz: + name: Fuzz (${{ matrix.target }}) + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - target: x86_64-unknown-linux-gnu + # `cargo fuzz`'s default. + sanitizer: address + - target: i686-unknown-linux-gnu + # rustc ships no AddressSanitizer runtime for 32-bit x86 + # (`librustc-*_rt.asan.a` exists only for the 64-bit targets), so the + # default `-s address` fails at link time. libFuzzer itself is built + # from source by `libfuzzer-sys` and works fine without it. + sanitizer: none + + env: + # Wall-clock budget per fuzz target. Long enough for libFuzzer to get past + # the header/section framing and into the decoder proper; short enough that + # the job stays comparable to the other checks. + FUZZ_SECONDS: 60 + + steps: + - name: Checkout code + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + + - name: Install 32-bit system libraries + if: matrix.target == 'i686-unknown-linux-gnu' + run: | + sudo apt-get update + sudo apt-get install -y gcc-multilib g++-multilib + + # cargo-fuzz needs nightly for `-Zsanitizer` and the sancov passes. Same + # pinned nightly as the Miri job. + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c + with: + toolchain: nightly-2026-08-30 + targets: ${{ matrix.target }} + + - name: Install cargo-fuzz + # Pinned so a cargo-fuzz release cannot change what this job runs. This + # is the same tool the Makefile's fuzzing targets already drive; nothing + # new enters the interpreter's dependency graph. + run: cargo install cargo-fuzz --locked --version 0.13.2 + + - name: Cache Rust dependencies + # Pinned commit resolved from the annotated Swatinem/rust-cache@v2 tag. + uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 + with: + # `fuzz` is excluded from the root workspace and carries its own lockfile. + workspaces: fuzz + key: ${{ matrix.target }} + save-if: ${{ github.ref == 'refs/heads/main' }} + cache-bin: false + + # Nothing else in CI compiles `fuzz/` -- it is excluded from the workspace, + # so `cargo build --workspace --all-targets` and clippy both skip it. This + # step alone keeps the fuzz targets from rotting. + - name: Build fuzz targets + run: cargo fuzz build --sanitizer ${{ matrix.sanitizer }} --target ${{ matrix.target }} + + - name: Run fuzz targets + run: | + for target in $(cargo fuzz list); do + echo "::group::$target" + cargo fuzz run "$target" \ + --sanitizer ${{ matrix.sanitizer }} \ + --target ${{ matrix.target }} -- \ + -max_total_time=${FUZZ_SECONDS} \ + -rss_limit_mb=4096 \ + -print_final_stats=1 + echo "::endgroup::" + done + + - name: Upload crash artifacts + if: always() + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: fuzz-artifacts-${{ matrix.target }} + path: fuzz/artifacts + if-no-files-found: ignore + retention-days: 7 + lint: name: Lint runs-on: ubuntu-latest diff --git a/Makefile b/Makefile index 8ca7e84..59fb948 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,8 @@ help: @echo "SpaceWasm Fuzzing Targets" @echo "" @echo "Configuration:" - @echo " SPACEWASM_TARGET= Target architecture (default: $(SPACEWASM_TARGET))" + @echo " SPACEWASM_TARGET= Target architecture (default: $(SPACEWASM_TARGET))" + @echo " SPACEWASM_FUZZ_SANITIZER= Sanitizer for the fuzz targets (default: $(SPACEWASM_FUZZ_SANITIZER))" @echo "" @echo "Fuzzing:" @echo " make fuzz Run the no_traps fuzzer" @@ -34,18 +35,25 @@ help: @echo "Utilities:" @echo " make clean-artifacts Delete all fuzzer artifacts" +# rustc ships no AddressSanitizer runtime for 32-bit x86, so `cargo fuzz`'s +# default `-s address` fails at link time on i686 looking for +# `librustc-*_rt.asan.a`. libFuzzer is built from source by `libfuzzer-sys` and +# does not need it, so drop the sanitizer for that target. +SPACEWASM_FUZZ_SANITIZER ?= $(if $(findstring i686,$(SPACEWASM_TARGET)),none,address) +FUZZ_FLAGS = --sanitizer $(SPACEWASM_FUZZ_SANITIZER) --target $(SPACEWASM_TARGET) + # Run fuzzer fuzz: - cargo +nightly fuzz run no_traps --target $(SPACEWASM_TARGET) + cargo +nightly fuzz run no_traps $(FUZZ_FLAGS) fuzz-validate: - cargo +nightly fuzz run validate --target $(SPACEWASM_TARGET) + cargo +nightly fuzz run validate $(FUZZ_FLAGS) fuzz-validate-differential: - cargo +nightly fuzz run validate_differential --target $(SPACEWASM_TARGET) + cargo +nightly fuzz run validate_differential $(FUZZ_FLAGS) fuzz-malformed: - cargo +nightly fuzz run malformed --target $(SPACEWASM_TARGET) + cargo +nightly fuzz run malformed $(FUZZ_FLAGS) # Convert seed to Wasm and trace execution (release mode) trace: