Skip to content

Commit 17524a4

Browse files
committed
feat: add automated crash reporting for array_ops fuzzing
Add Claude-powered crash analysis and GitHub issue creation for the array_ops fuzzing target, matching the existing file_io fuzzing setup. Changes: - Add crash detection and reporting to ops_fuzz job - Add report-ops-fuzz-failures job with Claude analysis - Use environment variables to parameterize shared Claude prompt logic This enables automated issue creation for array_ops fuzzing crashes, matching the existing file_io crash reporting functionality. Signed-off-by: Joe Isaacs <[email protected]>
1 parent 5ee3983 commit 17524a4

File tree

1 file changed

+233
-2
lines changed

1 file changed

+233
-2
lines changed

.github/workflows/fuzz.yml

Lines changed: 233 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,10 @@ jobs:
307307
- disk=large
308308
- extras=s3-cache
309309
- tag=ops-fuzz
310+
outputs:
311+
crashes_found: ${{ steps.check.outputs.crashes_found }}
312+
first_crash_name: ${{ steps.check.outputs.first_crash_name }}
313+
artifact_url: ${{ steps.upload_artifacts.outputs.artifact-url }}
310314
steps:
311315
- uses: runs-on/action@v2
312316
with:
@@ -335,13 +339,47 @@ jobs:
335339
AWS_ENDPOINT_URL: "https://01e9655179bbec953276890b183039bc.r2.cloudflarestorage.com"
336340
- name: Run fuzzing target
337341
id: fuzz
338-
run: RUST_BACKTRACE=1 cargo +nightly fuzz run --release --debug-assertions array_ops -- -max_total_time=7200
342+
run: |
343+
RUST_BACKTRACE=1 cargo +nightly fuzz run --release --debug-assertions array_ops -- -max_total_time=7200 2>&1 | tee fuzz_output.log
339344
continue-on-error: true
345+
- name: Check for crashes
346+
id: check
347+
run: |
348+
if [ -d "fuzz/artifacts" ] && [ "$(ls -A fuzz/artifacts 2>/dev/null)" ]; then
349+
echo "crashes_found=true" >> $GITHUB_OUTPUT
350+
351+
# Get the first crash file only
352+
FIRST_CRASH=$(find fuzz/artifacts -type f \( -name "crash-*" -o -name "leak-*" -o -name "timeout-*" -o -name "oom-*" \) | head -1)
353+
354+
if [ -n "$FIRST_CRASH" ]; then
355+
echo "first_crash=$FIRST_CRASH" >> $GITHUB_OUTPUT
356+
echo "first_crash_name=$(basename $FIRST_CRASH)" >> $GITHUB_OUTPUT
357+
358+
# Count all crashes for reporting
359+
CRASH_COUNT=$(find fuzz/artifacts -type f \( -name "crash-*" -o -name "leak-*" -o -name "timeout-*" -o -name "oom-*" \) | wc -l)
360+
echo "crash_count=$CRASH_COUNT" >> $GITHUB_OUTPUT
361+
echo "Found $CRASH_COUNT crash(es), will process first: $(basename $FIRST_CRASH)"
362+
fi
363+
else
364+
echo "crashes_found=false" >> $GITHUB_OUTPUT
365+
echo "crash_count=0" >> $GITHUB_OUTPUT
366+
echo "No crashes found"
367+
fi
340368
- name: Archive crash artifacts
369+
id: upload_artifacts
370+
if: steps.check.outputs.crashes_found == 'true'
341371
uses: actions/upload-artifact@v5
342372
with:
343373
name: operations-fuzzing-crash-artifacts
344374
path: fuzz/artifacts
375+
retention-days: 30
376+
- name: Archive fuzzer output log
377+
if: steps.check.outputs.crashes_found == 'true'
378+
uses: actions/upload-artifact@v4
379+
with:
380+
name: ops-fuzzing-logs
381+
path: fuzz_output.log
382+
retention-days: 30
345383
- name: Persist corpus
346384
shell: bash
347385
run: |
@@ -353,5 +391,198 @@ jobs:
353391
AWS_REGION: "us-east-1"
354392
AWS_ENDPOINT_URL: "https://01e9655179bbec953276890b183039bc.r2.cloudflarestorage.com"
355393
- name: Fail job if fuzz run found a bug
356-
if: steps.fuzz.outcome == 'failure'
394+
if: steps.check.outputs.crashes_found == 'true'
357395
run: exit 1
396+
397+
report-ops-fuzz-failures:
398+
name: "Report Array Operations Fuzz Failures"
399+
needs: ops_fuzz
400+
if: always() && needs.ops_fuzz.outputs.crashes_found == 'true'
401+
runs-on: ubuntu-latest
402+
permissions:
403+
issues: write
404+
contents: read
405+
id-token: write
406+
pull-requests: read
407+
steps:
408+
- name: Checkout repository
409+
uses: actions/checkout@v5
410+
411+
- name: Download fuzzer logs
412+
uses: actions/download-artifact@v4
413+
with:
414+
name: ops-fuzzing-logs
415+
path: ./logs
416+
417+
- name: Analyze and report crash with Claude
418+
env:
419+
CRASH_FILE: ${{ needs.ops_fuzz.outputs.first_crash_name }}
420+
ARTIFACT_URL: ${{ needs.ops_fuzz.outputs.artifact_url }}
421+
BRANCH: ${{ github.ref_name }}
422+
COMMIT: ${{ github.sha }}
423+
uses: anthropics/claude-code-action@v1
424+
with:
425+
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
426+
github_token: ${{ secrets.GITHUB_TOKEN }}
427+
show_full_output: true
428+
prompt: |
429+
# Fuzzer Crash Analysis and Reporting
430+
431+
A fuzzing run for the `array_ops` target has detected a crash. Analyze it and report by creating or updating a GitHub issue.
432+
433+
## Step 1: Analyze the Crash
434+
435+
1. Read the fuzzer log: `logs/fuzz_output.log`
436+
2. Extract:
437+
- Stack trace (lines with `#0`, `#1`, etc.)
438+
- Error message ("panicked at" or "ERROR:")
439+
- Crash location (top user code frame, not std/core/libfuzzer)
440+
- Debug output (look for "Output of `std::fmt::Debug`:" section before the crash)
441+
3. Read the source code at the crash location to understand root cause
442+
443+
## Step 2: Check for Duplicates
444+
445+
1. List existing fuzzer issues:
446+
```bash
447+
gh issue list --repo ${{ github.repository }} --label fuzzer --state open --json number,title,body --limit 50
448+
```
449+
450+
2. For each existing issue, compare:
451+
- **Crash location**: Same file + function? (line numbers can differ)
452+
- **Error pattern**: Same error after normalizing values?
453+
- "index 5 out of bounds" = "index 12 out of bounds" (SAME)
454+
- "len is 100" = "len is 5" (SAME)
455+
- Read source code if needed to verify same root cause
456+
457+
3. Determine duplication level:
458+
- **EXACT DUPLICATE**: Same crash location + same error pattern → Add reaction
459+
- **SIMILAR**: Same general area but unclear → Add comment
460+
- **NEW BUG**: Different location or different error type → Create new issue
461+
462+
## Step 3: Take Action
463+
464+
### If EXACT DUPLICATE (high confidence):
465+
Update or create a tracking comment to count occurrences:
466+
467+
1. First, check if there's already a tracking comment (look for a comment starting with "<!-- occurrences: ")
468+
2. If found, extract the count, increment it, and edit the comment
469+
3. If not found, create a new comment
470+
471+
Comment format:
472+
```markdown
473+
<!-- occurrences: N -->
474+
**Crash seen N time(s)**
475+
476+
Latest occurrence:
477+
- Crash file: $CRASH_FILE
478+
- Artifact: $ARTIFACT_URL
479+
- Branch: $BRANCH
480+
- Commit: $COMMIT
481+
```
482+
483+
Use:
484+
- `gh api repos/${{ github.repository }}/issues/ISSUE_NUM/comments` to list comments
485+
- `gh api repos/${{ github.repository }}/issues/comments/COMMENT_ID -X PATCH` to update
486+
- `gh issue comment ISSUE_NUM` to create new
487+
488+
### If SIMILAR (medium confidence):
489+
Comment on the existing issue with analysis:
490+
```bash
491+
gh issue comment ISSUE_NUM --repo ${{ github.repository }} --body "..."
492+
```
493+
494+
Include in comment:
495+
- Note that another crash was detected
496+
- Your confidence level and reasoning
497+
- Key differences if any
498+
- Crash file: $CRASH_FILE
499+
- Workflow: $WORKFLOW_RUN
500+
501+
### If NEW BUG (not a duplicate):
502+
Create a new issue with `gh issue create`:
503+
```bash
504+
gh issue create --repo ${{ github.repository }} \
505+
--title "Fuzzing Crash: [brief description]" \
506+
--label "bug,fuzzer" \
507+
--body "..."
508+
```
509+
510+
Issue body must include:
511+
```markdown
512+
## Fuzzing Crash Report
513+
514+
### Analysis
515+
516+
**Crash Location**: `file.rs:function_name`
517+
518+
**Error Message**:
519+
```
520+
[error message]
521+
```
522+
523+
**Stack Trace**:
524+
```
525+
[top 5-7 frames - keep in code block to prevent markdown rendering issues]
526+
```
527+
528+
Note: Keep stack traces in code blocks to prevent `#0`, `#1` from being interpreted as markdown headers.
529+
530+
**Root Cause**: [Your analysis]
531+
532+
<details>
533+
<summary>Debug Output</summary>
534+
535+
```
536+
[Include the complete "Output of std::fmt::Debug:" section from the fuzzer log]
537+
```
538+
</details>
539+
540+
### Summary
541+
542+
- **Target**: `array_ops`
543+
- **Crash File**: `$CRASH_FILE`
544+
- **Branch**: $BRANCH
545+
- **Commit**: $COMMIT
546+
- **Crash Artifact**: $ARTIFACT_URL
547+
548+
### Reproduction
549+
550+
1. Download the crash artifact:
551+
- **Direct download**: $ARTIFACT_URL
552+
- Or find `operations-fuzzing-crash-artifacts` at: $WORKFLOW_RUN
553+
- Extract the zip file
554+
555+
2. Reproduce locally:
556+
```bash
557+
# The artifact contains array_ops/$CRASH_FILE
558+
cargo +nightly fuzz run --sanitizer=none array_ops array_ops/$CRASH_FILE
559+
```
560+
561+
3. Get full backtrace:
562+
```bash
563+
RUST_BACKTRACE=full cargo +nightly fuzz run --sanitizer=none array_ops array_ops/$CRASH_FILE
564+
```
565+
566+
---
567+
*Auto-created by fuzzing workflow with Claude analysis*
568+
```
569+
570+
## Important Guidelines
571+
572+
- Be conservative: when unsure, prefer creating a new issue over marking as duplicate
573+
- Focus on ROOT CAUSE, not specific values in error messages
574+
- You have full repo access - read source code to understand crashes
575+
- Only use +1 reaction for truly identical crashes
576+
577+
## Environment Variables
578+
579+
- CRASH_FILE: $CRASH_FILE
580+
- ARTIFACT_URL: $ARTIFACT_URL (direct link to crash artifact)
581+
- BRANCH: $BRANCH
582+
- COMMIT: $COMMIT
583+
584+
Start by reading `logs/fuzz_output.log`.
585+
claude_args: |
586+
--model claude-sonnet-4-5-20250929
587+
--max-turns 25
588+
--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(jq:*),Bash(grep:*),Bash(cargo +nightly fuzz run:*),Bash(RUST_BACKTRACE=* cargo +nightly fuzz run:*)"

0 commit comments

Comments
 (0)