# Extended robustness: corpus render-stability sweep + libFuzzer run. # # DELIBERATELY ISOLATED from the main CI gate: # - never triggers on push or pull_request, so it can NEVER block a merge, # push, or pull; # - the whole job is continue-on-error, so a crash finding or a build/runner # problem reports red here but does not fail any required check; # - runs on a weekly schedule and on manual dispatch only. # # A full 24h fuzz run needs a self-hosted runner (GitHub-hosted runners cap a job # at 6h). Use the workflow_dispatch `duration_seconds` input for that; the weekly # schedule does a short smoke instead. name: Fuzz & corpus sweep on: schedule: - cron: "0 3 * * 0" # Sundays 03:00 UTC — short smoke workflow_dispatch: inputs: duration_seconds: description: "libFuzzer -max_total_time (e.g. 1800 smoke, 86400 for 24h on a self-hosted runner)" default: "1800" sanitizers: description: "Sanitizer set (fuzzer,address,undefined needs an ASan/UBSan-built PDFium; fuzzer = coverage-only)" default: "fuzzer,address,undefined" permissions: contents: read concurrency: group: fuzz-${{ github.ref }} cancel-in-progress: true jobs: fuzz: # Non-blocking by construction: nothing depends on this job and it is allowed to fail. continue-on-error: true runs-on: ubuntu-latest timeout-minutes: 1500 # permits a 24h dispatch on a self-hosted runner; hosted runners stop at 6h env: VCPKG_DEFAULT_BINARY_CACHE: ${{ github.workspace }}/.vcpkg-cache FUZZ_SANITIZERS: ${{ github.event.inputs.sanitizers || 'fuzzer' }} FUZZ_DURATION: ${{ github.event.inputs.duration_seconds || '600' }} steps: - uses: actions/checkout@v4 - name: Install Ninja + Clang shell: bash run: | sudo apt-get update sudo apt-get install -y ninja-build clang - name: Locate vcpkg shell: bash run: | echo "VCPKG_ROOT=$VCPKG_INSTALLATION_ROOT" >> "$GITHUB_ENV" mkdir -p "$VCPKG_DEFAULT_BINARY_CACHE" - name: Cache vcpkg artifacts uses: actions/cache@v4 with: path: ${{ env.VCPKG_DEFAULT_BINARY_CACHE }} key: vcpkg-fuzz-${{ hashFiles('vcpkg.json') }} restore-keys: vcpkg-fuzz- - name: Pin vcpkg dependency baseline shell: bash run: | if ! grep -q '"builtin-baseline"' vcpkg.json; then "$VCPKG_ROOT/vcpkg" x-update-baseline --add-initial-baseline fi - name: Configure (fuzz-linux) run: cmake --preset fuzz-linux -DPDFENGINE_FUZZ_SANITIZERS="$FUZZ_SANITIZERS" - name: Build fuzzer run: cmake --build --preset fuzz-linux --target pdfengine_fuzz - name: Fetch corpus (pinned + hash-verified) shell: bash run: python3 scripts/fetch_corpus.py --manifest tests/regression/corpus-manifest.json || echo "corpus fetch failed (network) — continuing with committed corpus" - name: Render-stability sweep (replay every corpus PDF once) shell: bash run: | BIN=out/build/fuzz-linux/bin/pdfengine_fuzz mkdir -p engine/fuzz/artifacts "$BIN" -runs=0 -artifact_prefix=engine/fuzz/artifacts/ corpus/ corpus/fuzz/ || true - name: Fuzz run shell: bash run: | BIN=out/build/fuzz-linux/bin/pdfengine_fuzz "$BIN" -max_total_time="$FUZZ_DURATION" -print_final_stats=1 \ -artifact_prefix=engine/fuzz/artifacts/ corpus/fuzz/ corpus/ || true - name: Upload crash artifacts if: always() uses: actions/upload-artifact@v4 with: name: fuzz-artifacts path: engine/fuzz/artifacts/ if-no-files-found: ignore