Fuzz Testing #41
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 Testing | |
| on: | |
| push: | |
| branches: [ main ] | |
| pull_request: | |
| branches: [ main ] | |
| schedule: | |
| # Run daily at 2 AM UTC | |
| - cron: '0 2 * * *' | |
| workflow_dispatch: | |
| inputs: | |
| duration: | |
| description: 'Fuzzing duration in seconds' | |
| required: false | |
| default: '600' | |
| target: | |
| description: 'Fuzz target to run (all_protocols or validate_with_python)' | |
| required: false | |
| default: 'all_protocols' | |
| env: | |
| CARGO_TERM_COLOR: always | |
| RUST_BACKTRACE: 1 | |
| jobs: | |
| fuzz-fast: | |
| name: Fast Fuzzing (all_protocols) | |
| runs-on: ubuntu-latest | |
| if: github.event_name == 'pull_request' || github.event_name == 'push' | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@nightly | |
| - name: Install cargo-fuzz | |
| run: cargo install cargo-fuzz | |
| - name: Cache fuzz corpus | |
| uses: actions/cache@v4 | |
| with: | |
| path: fuzz/corpus/all_protocols | |
| key: fuzz-corpus-all-protocols-${{ github.sha }} | |
| restore-keys: | | |
| fuzz-corpus-all-protocols- | |
| - name: Run fast fuzzing (5 minutes) | |
| run: | | |
| cargo fuzz run all_protocols -- \ | |
| -max_total_time=300 \ | |
| -print_final_stats=1 \ | |
| -verbosity=1 | |
| continue-on-error: true | |
| - name: Check for crashes | |
| if: always() | |
| run: | | |
| if [ -d "fuzz/artifacts/all_protocols" ]; then | |
| # Only fail on actual crashes, not memory leaks (often false positives) | |
| if ls fuzz/artifacts/all_protocols/crash-* 2>/dev/null || \ | |
| ls fuzz/artifacts/all_protocols/timeout-* 2>/dev/null || \ | |
| ls fuzz/artifacts/all_protocols/oom-* 2>/dev/null; then | |
| echo "::error::Fuzzing found crashes!" | |
| ls -la fuzz/artifacts/all_protocols/ | |
| exit 1 | |
| fi | |
| # Log leaks but don't fail | |
| if ls fuzz/artifacts/all_protocols/leak-* 2>/dev/null; then | |
| echo "::warning::Memory leaks detected (not failing build)" | |
| ls -la fuzz/artifacts/all_protocols/leak-* | |
| fi | |
| fi | |
| - name: Upload crash artifacts | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: fuzz-crashes-all-protocols | |
| path: fuzz/artifacts/all_protocols/ | |
| if-no-files-found: ignore | |
| fuzz-thorough: | |
| name: Thorough Fuzzing (validate_with_python) | |
| runs-on: ubuntu-latest | |
| if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@nightly | |
| - name: Install Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.11' | |
| - name: Install cargo-fuzz | |
| run: cargo install cargo-fuzz | |
| - name: Cache fuzz corpus | |
| uses: actions/cache@v4 | |
| with: | |
| path: fuzz/corpus/validate_with_python | |
| key: fuzz-corpus-validate-python-${{ github.sha }} | |
| restore-keys: | | |
| fuzz-corpus-validate-python- | |
| - name: Determine fuzzing duration | |
| id: duration | |
| run: | | |
| if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then | |
| echo "duration=${{ github.event.inputs.duration }}" >> $GITHUB_OUTPUT | |
| else | |
| echo "duration=1800" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Run thorough fuzzing with Python validation | |
| run: | | |
| cargo fuzz run validate_with_python -- \ | |
| -max_total_time=${{ steps.duration.outputs.duration }} \ | |
| -print_final_stats=1 \ | |
| -verbosity=1 | |
| continue-on-error: true | |
| - name: Check for crashes | |
| if: always() | |
| run: | | |
| if [ -d "fuzz/artifacts/validate_with_python" ]; then | |
| # Only fail on actual crashes, not memory leaks (often false positives) | |
| if ls fuzz/artifacts/validate_with_python/crash-* 2>/dev/null || \ | |
| ls fuzz/artifacts/validate_with_python/timeout-* 2>/dev/null || \ | |
| ls fuzz/artifacts/validate_with_python/oom-* 2>/dev/null; then | |
| echo "::error::Fuzzing found crashes!" | |
| ls -la fuzz/artifacts/validate_with_python/ | |
| exit 1 | |
| fi | |
| # Log leaks but don't fail | |
| if ls fuzz/artifacts/validate_with_python/leak-* 2>/dev/null; then | |
| echo "::warning::Memory leaks detected (not failing build)" | |
| ls -la fuzz/artifacts/validate_with_python/leak-* | |
| fi | |
| fi | |
| - name: Upload crash artifacts | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: fuzz-crashes-validate-python | |
| path: fuzz/artifacts/validate_with_python/ | |
| if-no-files-found: ignore | |
| fuzz-custom: | |
| name: Custom Fuzzing Run | |
| runs-on: ubuntu-latest | |
| if: github.event_name == 'workflow_dispatch' | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@nightly | |
| - name: Install Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.11' | |
| - name: Install cargo-fuzz | |
| run: cargo install cargo-fuzz | |
| - name: Cache fuzz corpus | |
| uses: actions/cache@v4 | |
| with: | |
| path: fuzz/corpus/${{ github.event.inputs.target }} | |
| key: fuzz-corpus-${{ github.event.inputs.target }}-${{ github.sha }} | |
| restore-keys: | | |
| fuzz-corpus-${{ github.event.inputs.target }}- | |
| - name: Run custom fuzzing | |
| run: | | |
| cargo fuzz run ${{ github.event.inputs.target }} -- \ | |
| -max_total_time=${{ github.event.inputs.duration }} \ | |
| -print_final_stats=1 \ | |
| -verbosity=1 | |
| continue-on-error: true | |
| - name: Check for crashes | |
| if: always() | |
| run: | | |
| if [ -d "fuzz/artifacts/${{ github.event.inputs.target }}" ]; then | |
| # Only fail on actual crashes, not memory leaks (often false positives) | |
| if ls fuzz/artifacts/${{ github.event.inputs.target }}/crash-* 2>/dev/null || \ | |
| ls fuzz/artifacts/${{ github.event.inputs.target }}/timeout-* 2>/dev/null || \ | |
| ls fuzz/artifacts/${{ github.event.inputs.target }}/oom-* 2>/dev/null; then | |
| echo "::error::Fuzzing found crashes!" | |
| ls -la fuzz/artifacts/${{ github.event.inputs.target }}/ | |
| exit 1 | |
| fi | |
| # Log leaks but don't fail | |
| if ls fuzz/artifacts/${{ github.event.inputs.target }}/leak-* 2>/dev/null; then | |
| echo "::warning::Memory leaks detected (not failing build)" | |
| ls -la fuzz/artifacts/${{ github.event.inputs.target }}/leak-* | |
| fi | |
| fi | |
| - name: Upload crash artifacts | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: fuzz-crashes-${{ github.event.inputs.target }} | |
| path: fuzz/artifacts/${{ github.event.inputs.target }}/ | |
| if-no-files-found: ignore | |
| minimize-corpus: | |
| name: Minimize Corpus | |
| runs-on: ubuntu-latest | |
| if: github.event_name == 'schedule' | |
| needs: [fuzz-thorough] | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@nightly | |
| - name: Install cargo-fuzz | |
| run: cargo install cargo-fuzz | |
| - name: Restore corpus | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| fuzz/corpus/all_protocols | |
| fuzz/corpus/validate_with_python | |
| key: fuzz-corpus-all-${{ github.sha }} | |
| restore-keys: | | |
| fuzz-corpus-all- | |
| - name: Minimize all_protocols corpus | |
| run: cargo fuzz cmin all_protocols | |
| continue-on-error: true | |
| - name: Minimize validate_with_python corpus | |
| run: cargo fuzz cmin validate_with_python | |
| continue-on-error: true | |
| - name: Report corpus stats | |
| run: | | |
| echo "## Corpus Statistics" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### all_protocols" >> $GITHUB_STEP_SUMMARY | |
| echo "Files: $(find fuzz/corpus/all_protocols -type f | wc -l)" >> $GITHUB_STEP_SUMMARY | |
| echo "Size: $(du -sh fuzz/corpus/all_protocols | cut -f1)" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### validate_with_python" >> $GITHUB_STEP_SUMMARY | |
| echo "Files: $(find fuzz/corpus/validate_with_python -type f | wc -l)" >> $GITHUB_STEP_SUMMARY | |
| echo "Size: $(du -sh fuzz/corpus/validate_with_python | cut -f1)" >> $GITHUB_STEP_SUMMARY |