Fuzz #1268
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Fuzz | |
| on: | |
| schedule: | |
| - cron: "0 */4 * * *" # every 4 hours | |
| workflow_dispatch: | |
| jobs: | |
| io_fuzz: | |
| name: "IO Fuzz" | |
| timeout-minutes: 230 # almost 4 hours | |
| runs-on: | |
| - runs-on=${{ github.run_id }} | |
| - family=m8g.large | |
| - image=ubuntu24-full-arm64 | |
| - disk=large | |
| - extras=s3-cache | |
| - tag=io-fuzz | |
| outputs: | |
| crashes_found: ${{ steps.check.outputs.crashes_found }} | |
| crash_count: ${{ steps.check.outputs.crash_count }} | |
| first_crash_name: ${{ steps.check.outputs.first_crash_name }} | |
| steps: | |
| - uses: runs-on/action@v2 | |
| with: | |
| sccache: s3 | |
| - uses: actions/checkout@v5 | |
| - uses: ./.github/actions/setup-rust | |
| with: | |
| repo-token: ${{ secrets.GITHUB_TOKEN }} | |
| toolchain: nightly | |
| - name: Install llvm | |
| uses: aminya/setup-cpp@v1 | |
| with: | |
| compiler: llvm | |
| - name: Install cargo fuzz | |
| run: cargo install --locked cargo-fuzz | |
| - name: Restore corpus | |
| shell: bash | |
| run: | | |
| aws s3api head-object --bucket vortex-fuzz-corpus --key "io_corpus.tar.zst" --query ETag --output text > current_etag | |
| aws s3 cp s3://vortex-fuzz-corpus/io_corpus.tar.zst . | |
| tar -xf io_corpus.tar.zst | |
| env: | |
| AWS_ACCESS_KEY_ID: ${{ secrets.R2_FUZZ_ACCESS_KEY_ID }} | |
| AWS_SECRET_ACCESS_KEY: ${{ secrets.R2_FUZZ_SECRET_ACCESS_KEY }} | |
| AWS_REGION: "us-east-1" | |
| AWS_ENDPOINT_URL: "https://01e9655179bbec953276890b183039bc.r2.cloudflarestorage.com" | |
| - name: Run fuzzing target | |
| id: fuzz | |
| run: | | |
| RUST_BACKTRACE=1 cargo +nightly fuzz run --release --debug-assertions file_io -- -max_total_time=7200 2>&1 | tee fuzz_output.log | |
| continue-on-error: true | |
| - name: Check for crashes | |
| id: check | |
| run: | | |
| if [ -d "fuzz/artifacts" ] && [ "$(ls -A fuzz/artifacts 2>/dev/null)" ]; then | |
| echo "crashes_found=true" >> $GITHUB_OUTPUT | |
| # Get the first crash file only | |
| FIRST_CRASH=$(find fuzz/artifacts -type f \( -name "crash-*" -o -name "leak-*" -o -name "timeout-*" -o -name "oom-*" \) | head -1) | |
| if [ -n "$FIRST_CRASH" ]; then | |
| echo "first_crash=$FIRST_CRASH" >> $GITHUB_OUTPUT | |
| echo "first_crash_name=$(basename $FIRST_CRASH)" >> $GITHUB_OUTPUT | |
| # Count all crashes for reporting | |
| CRASH_COUNT=$(find fuzz/artifacts -type f \( -name "crash-*" -o -name "leak-*" -o -name "timeout-*" -o -name "oom-*" \) | wc -l) | |
| echo "crash_count=$CRASH_COUNT" >> $GITHUB_OUTPUT | |
| echo "Found $CRASH_COUNT crash(es), will process first: $(basename $FIRST_CRASH)" | |
| fi | |
| else | |
| echo "crashes_found=false" >> $GITHUB_OUTPUT | |
| echo "crash_count=0" >> $GITHUB_OUTPUT | |
| echo "No crashes found" | |
| fi | |
| - name: Archive crash artifacts | |
| if: steps.check.outputs.crashes_found == 'true' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: io-fuzzing-crash-artifacts | |
| path: fuzz/artifacts | |
| retention-days: 30 | |
| - name: Archive fuzzer output log | |
| if: steps.check.outputs.crashes_found == 'true' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: io-fuzzing-logs | |
| path: fuzz_output.log | |
| retention-days: 30 | |
| - name: Persist corpus | |
| shell: bash | |
| run: | | |
| tar -acf io_corpus.tar.zst fuzz/corpus/file_io | |
| aws s3api put-object --bucket vortex-fuzz-corpus --key "io_corpus.tar.zst" --body io_corpus.tar.zst --checksum-algorithm CRC32 --if-match "$(cat current_etag)" | |
| env: | |
| AWS_ACCESS_KEY_ID: ${{ secrets.R2_FUZZ_ACCESS_KEY_ID }} | |
| AWS_SECRET_ACCESS_KEY: ${{ secrets.R2_FUZZ_SECRET_ACCESS_KEY }} | |
| AWS_REGION: "us-east-1" | |
| AWS_ENDPOINT_URL: "https://01e9655179bbec953276890b183039bc.r2.cloudflarestorage.com" | |
| - name: Fail job if fuzz run found a bug | |
| if: steps.check.outputs.crashes_found == 'true' | |
| run: exit 1 | |
| report-io-fuzz-failures: | |
| name: "Report IO Fuzz Failures" | |
| needs: io_fuzz | |
| if: always() && needs.io_fuzz.outputs.crashes_found == 'true' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| issues: write | |
| contents: read | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v5 | |
| - name: Download crash artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: io-fuzzing-crash-artifacts | |
| path: ./crash_artifacts | |
| - name: Download fuzzer logs | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: io-fuzzing-logs | |
| path: ./logs | |
| - name: Extract first crash only | |
| run: | | |
| # Only keep the first crash file for analysis | |
| FIRST_CRASH="${{ needs.io_fuzz.outputs.first_crash_name }}" | |
| echo "Processing only first crash: $FIRST_CRASH" | |
| # Create a clean directory with just the first crash | |
| mkdir -p ./first_crash | |
| if [ -f "crash_artifacts/$FIRST_CRASH" ]; then | |
| cp "crash_artifacts/$FIRST_CRASH" ./first_crash/ | |
| echo "Copied first crash to ./first_crash/" | |
| else | |
| echo "Warning: Could not find crash file $FIRST_CRASH" | |
| echo "Available files:" | |
| ls -la crash_artifacts/ || echo "No files in crash_artifacts" | |
| fi | |
| - name: Show crash info | |
| run: | | |
| echo "=== Crash Summary ===" | |
| echo "Total crashes found: ${{ needs.io_fuzz.outputs.crash_count }}" | |
| echo "Processing first crash: ${{ needs.io_fuzz.outputs.first_crash_name }}" | |
| echo "" | |
| echo "=== First crash file size ===" | |
| ls -lh first_crash/ || echo "No crash file" | |
| echo "" | |
| echo "=== Fuzzer log preview (last 100 lines, where crashes are reported) ===" | |
| tail -100 logs/fuzz_output.log || echo "No log file" | |
| - name: Setup Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.11' | |
| - name: Install dependencies | |
| run: pip install anthropic | |
| # - name: Check for duplicate issue | |
| # id: dedup | |
| # env: | |
| # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| # ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} | |
| # run: | | |
| # python scripts/check_fuzzer_duplicate.py \ | |
| # "${{ needs.io_fuzz.outputs.first_crash_name }}" \ | |
| # "logs/fuzz_output.log" \ | |
| # "${{ github.repository }}" > dedup_result.json | |
| # | |
| # # Check exit code (0 = not duplicate, 1 = duplicate) | |
| # if [ $? -eq 0 ]; then | |
| # echo "is_duplicate=false" >> $GITHUB_OUTPUT | |
| # echo "Not a duplicate, will create new issue" | |
| # else | |
| # echo "is_duplicate=true" >> $GITHUB_OUTPUT | |
| # ISSUE_NUM=$(jq -r '.issue_number' dedup_result.json) | |
| # echo "issue_number=$ISSUE_NUM" >> $GITHUB_OUTPUT | |
| # echo "Duplicate of issue #$ISSUE_NUM" | |
| # fi | |
| # | |
| # # Show result | |
| # cat dedup_result.json | |
| - name: Create GitHub issue | |
| if: 0 && steps.dedup.outputs.is_duplicate == 'false' | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| # Extract crash type from filename | |
| CRASH_FILE="${{ needs.io_fuzz.outputs.first_crash_name }}" | |
| if [[ "$CRASH_FILE" == crash-* ]]; then | |
| CRASH_TYPE="Crash" | |
| elif [[ "$CRASH_FILE" == leak-* ]]; then | |
| CRASH_TYPE="Memory Leak" | |
| elif [[ "$CRASH_FILE" == timeout-* ]]; then | |
| CRASH_TYPE="Timeout" | |
| elif [[ "$CRASH_FILE" == oom-* ]]; then | |
| CRASH_TYPE="Out of Memory" | |
| else | |
| CRASH_TYPE="Unknown" | |
| fi | |
| # Create issue with gh CLI | |
| gh issue create \ | |
| --repo ${{ github.repository }} \ | |
| --title "Fuzzing $CRASH_TYPE: file_io - $(date -u +%Y-%m-%d)" \ | |
| --label "bug,fuzzer" \ | |
| --body-file - <<'EOF' | |
| ## Fuzzing Crash Report | |
| The `file_io` fuzzing target detected a $CRASH_TYPE during a scheduled fuzzing run. | |
| ### Summary | |
| - **Crash Type**: $CRASH_TYPE | |
| - **Target**: `file_io` | |
| - **Crash File**: `${{ needs.io_fuzz.outputs.first_crash_name }}` | |
| - **Total Crashes Found**: ${{ needs.io_fuzz.outputs.crash_count }} | |
| - **Workflow Run**: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} | |
| - **Timestamp**: $(date -u +"%Y-%m-%d %H:%M:%S UTC") | |
| - **Branch**: ${{ github.ref_name }} | |
| - **Commit**: ${{ github.sha }} | |
| ### Crash Artifacts | |
| Download crash artifacts from the workflow run: | |
| **https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}** | |
| Artifacts available: | |
| - `io-fuzzing-crash-artifacts` - All crash files found (includes ${{ needs.io_fuzz.outputs.crash_count }} crashes) | |
| - `io-fuzzing-logs` - Complete fuzzer output with stack traces | |
| ### Reproduction Steps | |
| 1. Download the `io-fuzzing-crash-artifacts` from the workflow run above | |
| 2. Extract the crash file to your local `fuzz/artifacts/file_io/` directory | |
| 3. Reproduce the crash locally: | |
| ```bash | |
| cargo +nightly fuzz run file_io fuzz/artifacts/file_io/${{ needs.io_fuzz.outputs.first_crash_name }} | |
| ``` | |
| 4. Get full backtrace: | |
| ```bash | |
| RUST_BACKTRACE=full cargo +nightly fuzz run file_io fuzz/artifacts/file_io/${{ needs.io_fuzz.outputs.first_crash_name }} | |
| ``` | |
| 5. Minimize the test case (optional): | |
| ```bash | |
| cargo +nightly fuzz tmin file_io fuzz/artifacts/file_io/${{ needs.io_fuzz.outputs.first_crash_name }} | |
| ``` | |
| ### Investigation Checklist | |
| - [ ] Download crash artifacts from workflow run | |
| - [ ] Reproduce crash locally with full backtrace | |
| - [ ] Analyze stack trace and identify root cause | |
| - [ ] Determine severity (security vs stability) | |
| - [ ] Check if this is a duplicate of an existing issue | |
| - [ ] Minimize test case if needed | |
| - [ ] Create fix PR with reference to this issue | |
| - [ ] Add regression test | |
| - [ ] Verify fix with: `cargo +nightly fuzz run file_io <crash-file>` | |
| ### Environment | |
| - **Runner**: ubuntu24-full-arm64 | |
| - **Rust Toolchain**: nightly | |
| - **Fuzz Duration**: 7200 seconds (2 hours) | |
| - **Fuzzer**: cargo-fuzz (libFuzzer) | |
| ### Additional Context | |
| This issue was automatically created by the fuzzing workflow. If multiple crashes were found, this issue represents the first crash detected. All crash artifacts are available for download. | |
| **Note**: If this issue is a duplicate of an existing bug, please close it and reference the original issue. | |
| --- | |
| *Automatically created by fuzzing workflow* | |
| *Workflow file: [fuzz.yml](https://github.com/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/fuzz.yml)* | |
| *Run: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}* | |
| EOF | |
| - name: Comment on duplicate issue | |
| if: steps.dedup.outputs.is_duplicate == 'true' | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| ISSUE_NUM="${{ steps.dedup.outputs.issue_number }}" | |
| gh issue comment "$ISSUE_NUM" \ | |
| --repo ${{ github.repository }} \ | |
| --body-file - <<'EOF' | |
| ## Additional Crash Detected | |
| Another crash was found in the `file_io` fuzzing target that appears to be a duplicate of this issue. | |
| ### Details | |
| - **Crash File**: `${{ needs.io_fuzz.outputs.first_crash_name }}` | |
| - **Total Crashes**: ${{ needs.io_fuzz.outputs.crash_count }} found in this run | |
| - **Workflow Run**: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} | |
| - **Timestamp**: $(date -u +"%Y-%m-%d %H:%M:%S UTC") | |
| ### Artifacts | |
| Download crash artifacts from: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} | |
| The fuzzing infrastructure detected this as a likely duplicate based on similar stack traces and error messages. | |
| --- | |
| *Automatically added by fuzzing workflow* | |
| EOF | |
| ops_fuzz: | |
| name: "Array Operations Fuzz" | |
| timeout-minutes: 230 # almost 4 hours | |
| runs-on: | |
| - runs-on=${{ github.run_id }} | |
| - family=m8g.large | |
| - image=ubuntu24-full-arm64 | |
| - disk=large | |
| - extras=s3-cache | |
| - tag=ops-fuzz | |
| steps: | |
| - uses: runs-on/action@v2 | |
| with: | |
| sccache: s3 | |
| - uses: actions/checkout@v5 | |
| - uses: ./.github/actions/setup-rust | |
| with: | |
| repo-token: ${{ secrets.GITHUB_TOKEN }} | |
| toolchain: nightly | |
| - name: Install llvm | |
| uses: aminya/setup-cpp@v1 | |
| with: | |
| compiler: llvm | |
| - name: Install cargo fuzz | |
| run: cargo install --locked cargo-fuzz | |
| - name: Restore corpus | |
| shell: bash | |
| run: | | |
| aws s3api head-object --bucket vortex-fuzz-corpus --key "array_ops_corpus.tar.zst" --query ETag --output text > current_etag | |
| aws s3 cp s3://vortex-fuzz-corpus/array_ops_corpus.tar.zst . | |
| tar -xf array_ops_corpus.tar.zst | |
| env: | |
| AWS_ACCESS_KEY_ID: ${{ secrets.R2_FUZZ_ACCESS_KEY_ID }} | |
| AWS_SECRET_ACCESS_KEY: ${{ secrets.R2_FUZZ_SECRET_ACCESS_KEY }} | |
| AWS_REGION: "us-east-1" | |
| AWS_ENDPOINT_URL: "https://01e9655179bbec953276890b183039bc.r2.cloudflarestorage.com" | |
| - name: Run fuzzing target | |
| id: fuzz | |
| run: RUST_BACKTRACE=1 cargo +nightly fuzz run --release --debug-assertions array_ops -- -max_total_time=7200 | |
| continue-on-error: true | |
| - name: Archive crash artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: operations-fuzzing-crash-artifacts | |
| path: fuzz/artifacts | |
| - name: Persist corpus | |
| shell: bash | |
| run: | | |
| tar -acf array_ops_corpus.tar.zst fuzz/corpus/array_ops | |
| aws s3api put-object --bucket vortex-fuzz-corpus --key "array_ops_corpus.tar.zst" --body array_ops_corpus.tar.zst --checksum-algorithm CRC32 --if-match "$(cat current_etag)" | |
| env: | |
| AWS_ACCESS_KEY_ID: ${{ secrets.R2_FUZZ_ACCESS_KEY_ID }} | |
| AWS_SECRET_ACCESS_KEY: ${{ secrets.R2_FUZZ_SECRET_ACCESS_KEY }} | |
| AWS_REGION: "us-east-1" | |
| AWS_ENDPOINT_URL: "https://01e9655179bbec953276890b183039bc.r2.cloudflarestorage.com" | |
| - name: Fail job if fuzz run found a bug | |
| if: steps.fuzz.outcome == 'failure' | |
| run: exit 1 |