Fuzz #1279
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 | |
| id-token: write # Required for Claude Code Action OIDC authentication | |
| pull-requests: read # For gh api commands | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v5 | |
| - name: Download fuzzer logs | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: io-fuzzing-logs | |
| path: ./logs | |
| # Single Claude Code Action that does everything: | |
| # - Analyzes crash from logs | |
| # - Checks for duplicate issues | |
| # - Creates new issue OR comments on existing issue | |
| - name: Analyze and report crash with Claude | |
| env: | |
| CRASH_FILE: ${{ needs.io_fuzz.outputs.first_crash_name }} | |
| CRASH_COUNT: ${{ needs.io_fuzz.outputs.crash_count }} | |
| WORKFLOW_RUN: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} | |
| BRANCH: ${{ github.ref_name }} | |
| COMMIT: ${{ github.sha }} | |
| uses: anthropics/claude-code-action@v1 | |
| with: | |
| claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} | |
| github_token: ${{ secrets.GITHUB_TOKEN }} | |
| show_full_output: true | |
| prompt: | | |
| # Fuzzer Crash Analysis and Reporting | |
| A fuzzing run for the `file_io` target has detected a crash. Analyze it and report by creating or updating a GitHub issue. | |
| ## Step 1: Analyze the Crash | |
| 1. Read the fuzzer log: `logs/fuzz_output.log` | |
| 2. Extract: | |
| - Stack trace (lines with `#0`, `#1`, etc.) | |
| - Error message ("panicked at" or "ERROR:") | |
| - Crash location (top user code frame, not std/core/libfuzzer) | |
| 3. Read the source code at the crash location to understand root cause | |
| ## Step 2: Check for Duplicates | |
| 1. List existing fuzzer issues: | |
| ```bash | |
| gh issue list --repo ${{ github.repository }} --label fuzzer --state open --json number,title,body --limit 50 | |
| ``` | |
| 2. For each existing issue, compare: | |
| - **Crash location**: Same file + function? (line numbers can differ) | |
| - **Error pattern**: Same error after normalizing values? | |
| - "index 5 out of bounds" = "index 12 out of bounds" (SAME) | |
| - "len is 100" = "len is 5" (SAME) | |
| - Read source code if needed to verify same root cause | |
| 3. Determine duplication level: | |
| - **EXACT DUPLICATE**: Same crash location + same error pattern → Add reaction | |
| - **SIMILAR**: Same general area but unclear → Add comment | |
| - **NEW BUG**: Different location or different error type → Create new issue | |
| ## Step 3: Take Action | |
| ### If EXACT DUPLICATE (high confidence): | |
| Just add a 👍 reaction to the existing issue: | |
| ```bash | |
| gh issue view ISSUE_NUM --repo ${{ github.repository }} --json reactions | |
| gh api repos/${{ github.repository }}/issues/ISSUE_NUM/reactions -f content='+1' | |
| ``` | |
| ### If SIMILAR (medium confidence): | |
| Comment on the existing issue with analysis: | |
| ```bash | |
| gh issue comment ISSUE_NUM --repo ${{ github.repository }} --body "..." | |
| ``` | |
| Include in comment: | |
| - Note that another crash was detected | |
| - Your confidence level and reasoning | |
| - Key differences if any | |
| - Crash file: $CRASH_FILE | |
| - Workflow: $WORKFLOW_RUN | |
| ### If NEW BUG (not a duplicate): | |
| Create a new issue with `gh issue create`: | |
| ```bash | |
| gh issue create --repo ${{ github.repository }} \ | |
| --title "Fuzzing Crash: [brief description]" \ | |
| --label "bug,fuzzer" \ | |
| --body "..." | |
| ``` | |
| Issue body must include: | |
| ```markdown | |
| ## Fuzzing Crash Report | |
| ### Analysis | |
| **Crash Location**: `file.rs:function_name` | |
| **Error Message**: | |
| ``` | |
| [error message] | |
| ``` | |
| **Stack Trace**: | |
| ``` | |
| [top 5-7 frames - keep in code block to prevent markdown rendering issues] | |
| ``` | |
| Note: Keep stack traces in code blocks to prevent `#0`, `#1` from being interpreted as markdown headers. | |
| **Root Cause**: [Your analysis] | |
| ### Summary | |
| - **Target**: `file_io` | |
| - **Crash File**: `$CRASH_FILE` | |
| - **Total Crashes**: $CRASH_COUNT | |
| - **Branch**: $BRANCH | |
| - **Commit**: $COMMIT | |
| - **Timestamp**: [current UTC time] | |
| - **Workflow**: $WORKFLOW_RUN | |
| ### Reproduction | |
| 1. Download the crash artifact from the workflow run: | |
| - **Workflow run**: $WORKFLOW_RUN | |
| - Look for the artifact named `io-fuzzing-crash-artifacts` at the bottom of the workflow run page | |
| - Download and extract the zip file | |
| 2. Reproduce locally: | |
| ```bash | |
| # The artifact contains file_io/$CRASH_FILE | |
| cargo +nightly fuzz run file_io file_io/$CRASH_FILE | |
| ``` | |
| 3. Get full backtrace: | |
| ```bash | |
| RUST_BACKTRACE=full cargo +nightly fuzz run file_io file_io/$CRASH_FILE | |
| ``` | |
| --- | |
| *Auto-created by fuzzing workflow with Claude analysis* | |
| ``` | |
| ## Important Guidelines | |
| - Be conservative: when unsure, prefer creating a new issue over marking as duplicate | |
| - Focus on ROOT CAUSE, not specific values in error messages | |
| - You have full repo access - read source code to understand crashes | |
| - Only use +1 reaction for truly identical crashes | |
| ## Environment Variables | |
| - CRASH_FILE: $CRASH_FILE | |
| - CRASH_COUNT: $CRASH_COUNT | |
| - WORKFLOW_RUN: $WORKFLOW_RUN | |
| - BRANCH: $BRANCH | |
| - COMMIT: $COMMIT | |
| Start by reading `logs/fuzz_output.log`. | |
| claude_args: | | |
| --model claude-sonnet-4-5-20250929 | |
| --max-turns 25 | |
| --allowedTools "Read,Write,Bash(gh issue list:*),Bash(gh issue view:*),Bash(gh issue create:*),Bash(gh issue comment:*),Bash(gh api:*),Bash(echo:*),Bash(cat:*),Bash(cargo +nightly fuzz run:*),Bash(RUST_BACKTRACE=* cargo +nightly fuzz run:*)" | |
| 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 |