Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
18 changes: 13 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ help:
@echo "SpaceWasm Fuzzing Targets"
@echo ""
@echo "Configuration:"
@echo " SPACEWASM_TARGET=<triple> Target architecture (default: $(SPACEWASM_TARGET))"
@echo " SPACEWASM_TARGET=<triple> Target architecture (default: $(SPACEWASM_TARGET))"
@echo " SPACEWASM_FUZZ_SANITIZER=<name> Sanitizer for the fuzz targets (default: $(SPACEWASM_FUZZ_SANITIZER))"
@echo ""
@echo "Fuzzing:"
@echo " make fuzz Run the no_traps fuzzer"
Expand All @@ -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:
Expand Down