Release v4.1.0: version bump and ChangeLog #296
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 Testing | |
| on: | |
| schedule: | |
| - cron: '0 4 * * *' # Nightly 4am UTC | |
| workflow_dispatch: # Manual trigger | |
| pull_request: | |
| branches: [ '*' ] | |
| types: [opened, reopened, ready_for_review] | |
| push: | |
| branches: [ master ] | |
| jobs: | |
| # The matrix context is not available in a job-level if:, so route by run | |
| # type here instead: 600s full runs go to the nightly schedule / manual | |
| # dispatch; 60s smoke runs go to non-draft PR open/reopen/ready and merges | |
| # to master. Intermediate PR pushes (synchronize) are not a trigger. | |
| select: | |
| if: github.event_name != 'pull_request' || github.event.pull_request.draft == false | |
| runs-on: ubuntu-latest | |
| outputs: | |
| matrix: ${{ steps.pick.outputs.matrix }} | |
| steps: | |
| - name: Pick fuzz matrix for ${{ github.event_name }} | |
| id: pick | |
| run: | | |
| # schedule / dispatch -> full (600s); PR / push -> smoke (60s). | |
| case "${{ github.event_name }}" in | |
| schedule|workflow_dispatch) WANT=false ;; | |
| *) WANT=true ;; | |
| esac | |
| PQC="--enable-dilithium --enable-mlkem --enable-experimental --enable-harden" | |
| MATRIX=$(jq -nc --argjson want "$WANT" --arg pqc "$PQC" '{ | |
| include: [ | |
| {name:"fuzz-full", fuzz_time:600, smoke_only:false, wolfssl_extra_flags:"", wolftpm_extra_flags:"", max_len:4096}, | |
| {name:"fuzz-smoke", fuzz_time:60, smoke_only:true, wolfssl_extra_flags:"", wolftpm_extra_flags:"", max_len:4096}, | |
| {name:"fuzz-full-pqc", fuzz_time:600, smoke_only:false, wolfssl_extra_flags:$pqc, wolftpm_extra_flags:"--enable-v185", max_len:8192}, | |
| {name:"fuzz-smoke-pqc", fuzz_time:60, smoke_only:true, wolfssl_extra_flags:$pqc, wolftpm_extra_flags:"--enable-v185", max_len:8192} | |
| ] | map(select(.smoke_only == $want)) | |
| }') | |
| echo "matrix=$MATRIX" >> "$GITHUB_OUTPUT" | |
| fuzz: | |
| needs: select | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| strategy: | |
| fail-fast: false | |
| matrix: ${{ fromJson(needs.select.outputs.matrix) }} | |
| steps: | |
| - name: Checkout wolfTPM | |
| uses: actions/checkout@v4 | |
| - name: ASLR workaround | |
| run: sudo sysctl vm.mmap_rnd_bits=28 | |
| - name: Setup wolfSSL with fuzzer support | |
| uses: ./.github/actions/setup-wolfssl | |
| with: | |
| cc: clang | |
| configure-flags: --enable-wolftpm --enable-pkcallbacks --enable-keygen ${{ matrix.wolfssl_extra_flags }} | |
| cflags: -fsanitize=fuzzer-no-link,address -fno-omit-frame-pointer -g -O1 -DWC_RSA_NO_PADDING | |
| ldflags: -fsanitize=address | |
| - name: Build fuzz target | |
| run: | | |
| ./autogen.sh | |
| CC=clang ./configure --enable-fwtpm --enable-fuzz \ | |
| ${{ matrix.wolftpm_extra_flags }} \ | |
| CFLAGS="-fsanitize=fuzzer-no-link,address -fno-omit-frame-pointer -g -O1" \ | |
| LDFLAGS="-fsanitize=address" | |
| make -j$(nproc) | |
| - name: Generate seed corpus | |
| run: python3 tests/fuzz/gen_corpus.py | |
| - name: Verify v1.85 PQC opcode coverage in corpus | |
| if: contains(matrix.name, 'pqc') | |
| run: | | |
| # Without this guard, fuzz-*-pqc could spend 10 minutes fuzzing | |
| # classical paths with -DWOLFTPM_V185 set and never exercise an | |
| # Encapsulate or SignSequenceComplete opcode. Fail fast if any of | |
| # the 8 new v1.85 command codes is absent from the seed corpus. | |
| # Seeds are binary; opcodes appear as raw 4-byte big-endian | |
| # sequences. Hex-dump the concatenated corpus and grep the | |
| # resulting hex stream for each opcode's bytes. | |
| CORPUS_HEX=$(cat tests/fuzz/corpus/*.bin | xxd -p | tr -d '\n') | |
| MISSING=() | |
| for cc in 000001a3 000001a4 000001a5 000001a6 \ | |
| 000001a7 000001a8 000001a9 000001aa; do | |
| if ! echo "$CORPUS_HEX" | grep -q "$cc"; then | |
| MISSING+=("0x${cc^^}") | |
| fi | |
| done | |
| if [ ${#MISSING[@]} -gt 0 ]; then | |
| echo "ERROR: PQC seed corpus missing the following command codes:" | |
| printf ' %s\n' "${MISSING[@]}" | |
| echo "Update tests/fuzz/gen_corpus.py to emit a seed for each." | |
| exit 1 | |
| fi | |
| echo "All 8 v1.85 PQC opcodes (0x1A3-0x1AA) present in seed corpus." | |
| - name: Run fuzzer | |
| env: | |
| ASAN_OPTIONS: "detect_leaks=1:abort_on_error=1:symbolize=1" | |
| run: | | |
| echo "Fuzzing for ${{ matrix.fuzz_time }} seconds..." | |
| timeout ${{ matrix.fuzz_time }} \ | |
| ./tests/fuzz/fwtpm_fuzz \ | |
| tests/fuzz/corpus/ \ | |
| -dict=tests/fuzz/tpm2.dict \ | |
| -max_len=${{ matrix.max_len }} \ | |
| -timeout=30 \ | |
| -rss_limit_mb=2048 \ | |
| -print_final_stats=1 \ | |
| || FUZZ_RC=$? | |
| # timeout returns 124 on normal expiry, fuzzer returns 0 on no crash | |
| if [ "${FUZZ_RC:-0}" -eq 124 ] || [ "${FUZZ_RC:-0}" -eq 0 ]; then | |
| echo "Fuzzer completed without crashes" | |
| else | |
| echo "Fuzzer found crashes (exit code $FUZZ_RC)" | |
| ls -la crash-* 2>/dev/null || true | |
| exit 1 | |
| fi | |
| - name: Upload crash artifacts | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: fuzz-crashes-${{ matrix.name }} | |
| path: | | |
| crash-* | |
| oom-* | |
| timeout-* | |
| retention-days: 30 | |
| if-no-files-found: ignore |