rust-fs-ext4 0.8.0

Pure-Rust ext4 filesystem driver. Exposes a C ABI (fs_ext4_*) suitable for FFI from C/C++/Go/etc.
Documentation
name: fuzz

# The explorer half of the fuzzing setup. The gate half --
# tests/fuzz_decoders.rs -- runs in the `test` job of ci.yml on every
# pull request, because it is deterministic and finishes in five
# seconds. This one is neither, and that is why it is here instead.
#
# A fuzzer's result depends on how long it ran. Putting it in the pull
# request gate would mean a fresh finding fails whichever unrelated
# change happened to be open, and a green result would only ever mean
# "nothing found in the seconds we could spare".
#
# So it runs nightly and on demand, and when it finds something the
# reproducer is uploaded. That reproducer belongs in fuzz/corpus/, where
# the gate replays it on every pull request from then on.
#
# ext4 is mounted from disks and images this reader did not write -- a
# USB stick, a VM image, a card out of a device -- and every structure
# on it is read from an offset the structure before it supplied, which
# is exactly the threat model a fuzzer is for.
on:
  workflow_dispatch:
    inputs:
      seconds:
        description: "Seconds per target"
        required: false
        default: "120"
  schedule:
    - cron: "0 3 * * *"

# Declared, so the job does not inherit whatever the repository's default
# token grants: tests/scripts/test-write-jobs-pinned.sh treats a workflow
# with no `permissions` as one that may write.
permissions:
  contents: read

env:
  CARGO_TERM_COLOR: always
  CARGO_INCREMENTAL: 0
  CHORE_VERSION: "0.11.0"

jobs:
  fuzz:
    name: fuzz the superblock, inode, directory and journal decoders
    runs-on: ubuntu-latest
    timeout-minutes: 90
    steps:
      - uses: actions/checkout@v4

      # The siblings come through `chore`, at the refs pinned in
      # chores.yml, the same way ci.yml gets them -- rather than a
      # second place that says which version of rust-fs-core this
      # repository builds against.
      - name: Install chore
        run: scripts/ci-install-chore.sh
      - name: chore siblings
        run: chore siblings

      # Nightly, not the pin in rust-toolchain.toml. cargo-fuzz needs
      # `-Z sanitizer`, which stable does not have. The explicit
      # `+nightly` in scripts/fuzz-all.sh overrides the pin for these
      # invocations and nothing else.
      - name: Install Rust nightly
        uses: dtolnay/rust-toolchain@nightly

      # Building cargo-fuzz takes several minutes and changes about as
      # often as the compiler does, so it is cached on its own version
      # rather than on this repository's lockfile.
      - name: Cache cargo-fuzz
        id: cache-cargo-fuzz
        uses: actions/cache@v4
        with:
          path: ~/.cargo/bin/cargo-fuzz
          key: cargo-fuzz-${{ runner.os }}-${{ runner.arch }}-v1

      - name: Install cargo-fuzz
        if: steps.cache-cargo-fuzz.outputs.cache-hit != 'true'
        run: cargo +nightly install cargo-fuzz --locked

      - name: Show what the corpus holds
        run: |
          find fuzz/corpus -type f | sort
          echo "total: $(find fuzz/corpus -type f | wc -l) seeds"

      # The input arrives through env:, never pasted into the script: an
      # expression in `run:` is substituted before the shell parses it,
      # so `1; curl ... | sh` would be commands. scripts/fuzz-all.sh then
      # refuses anything but a whole number of seconds that fits the
      # job's timeout, before anything is fuzzed.
      - name: Fuzz
        env:
          SECONDS_PER_TARGET: ${{ inputs.seconds || '120' }}
        run: ./scripts/fuzz-all.sh "$SECONDS_PER_TARGET"

      # WITHOUT THIS THE FINDING IS LOST. A crash reproducer lives in
      # fuzz/artifacts/<target>/ on the runner, and the runner is thrown
      # away. The log would say a target crashed and the bytes that
      # caused it would be gone, leaving the next person to re-derive an
      # input the fuzzer had already found. `cancelled()` too: a run
      # stopped at its timeout still holds every crash the earlier
      # targets found.
      - name: Keep the reproducers
        if: failure() || cancelled()
        uses: actions/upload-artifact@v4
        with:
          name: fuzz-artifacts
          path: fuzz/artifacts/
          if-no-files-found: ignore
          retention-days: 90