Repository navigation
Fuzz #50
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 * * *" | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| jobs: | |
| fuzz: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 45 | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| submodules: recursive | |
| - name: Install a Clang with the libFuzzer runtime (Apple's Xcode clang doesn't ship one) | |
| run: | | |
| sudo apt-get update && sudo apt-get install -y clang llvm | |
| command -v llvm-symbolizer || sudo ln -s "$(find /usr -name 'llvm-symbolizer-*' 2>/dev/null | sort -V | tail -n1)" /usr/bin/llvm-symbolizer | |
| - uses: ./.github/actions/setup-android-toolchain | |
| - name: Configure and build tiff_fuzz | |
| working-directory: lib/native/src/main/cpp | |
| run: | | |
| cmake -S . -B build-fuzz -DTIFFRENDERER_FUZZ=ON \ | |
| -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ \ | |
| -DJAVA_HOME="${JAVA_HOME}" | |
| cmake --build build-fuzz --target tiff_fuzz -j | |
| - name: Restore the accumulated fuzz corpus | |
| uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | |
| with: | |
| path: /tmp/fuzz-corpus | |
| key: tiff-fuzz-corpus-${{ github.run_id }} | |
| restore-keys: tiff-fuzz-corpus- | |
| - name: Run tiff_fuzz against the accumulated corpus | |
| id: fuzz | |
| continue-on-error: true | |
| working-directory: lib/native/src/main/cpp | |
| env: | |
| UBSAN_OPTIONS: print_stacktrace=1:halt_on_error=1 | |
| ASAN_OPTIONS: symbolize=1 | |
| run: | | |
| mkdir -p /tmp/fuzz-artifacts /tmp/fuzz-corpus | |
| pad=$(grep -oP 'kControlBytes = \K[0-9]+' fuzz/tiff_fuzz.cpp) | |
| for f in "${GITHUB_WORKSPACE}"/lib/core/src/commonTest/resources/*.tif "${GITHUB_WORKSPACE}"/lib/core/src/commonTest/resources/*.bin; do | |
| [ -f "$f" ] || continue | |
| dest="/tmp/fuzz-corpus/$(basename "$f")" | |
| [ -e "$dest" ] && continue | |
| cat "$f" > "$dest" | |
| head -c "$pad" /dev/zero >> "$dest" | |
| done | |
| ./build-fuzz/tiff_fuzz -fork=1 -ignore_crashes=1 -ignore_ooms=1 -ignore_timeouts=1 \ | |
| -max_total_time=1800 -rss_limit_mb=4096 \ | |
| -artifact_prefix=/tmp/fuzz-artifacts/ /tmp/fuzz-corpus | |
| - name: Upload crashers | |
| if: always() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: fuzz-crashers | |
| path: /tmp/fuzz-artifacts/ | |
| if-no-files-found: ignore | |
| - name: Fail only if a crash implicates our own code, not a vendored dependency | |
| if: always() | |
| working-directory: lib/native/src/main/cpp | |
| env: | |
| UBSAN_OPTIONS: print_stacktrace=1:halt_on_error=1 | |
| ASAN_OPTIONS: symbolize=1 | |
| FUZZ_OUTCOME: ${{ steps.fuzz.outcome }} | |
| run: | | |
| shopt -s nullglob | |
| crashers=(/tmp/fuzz-artifacts/crash-* /tmp/fuzz-artifacts/oom-* /tmp/fuzz-artifacts/timeout-* /tmp/fuzz-artifacts/leak-*) | |
| if [ ${#crashers[@]} -eq 0 ]; then | |
| if [ "$FUZZ_OUTCOME" = "failure" ]; then | |
| echo "tiff_fuzz failed but left no crash artifact behind; failing to be safe." | |
| exit 1 | |
| fi | |
| echo "No crashes found." | |
| exit 0 | |
| fi | |
| hitOurCode=0 | |
| for f in "${crashers[@]}"; do | |
| echo "::group::Reproducing $(basename "$f")" | |
| report=$(./build-fuzz/tiff_fuzz -timeout=30 -rss_limit_mb=4096 "$f" 2>&1) || true | |
| echo "$report" | |
| echo "::endgroup::" | |
| frame=$(grep -oE '^ *#[0-9]+ 0x[0-9a-f]+ in .* [^ ]+\.(c|cc|cpp|h|hpp|hh):[0-9]+(:[0-9]+)?$' <<< "$report" | head -n1) | |
| if [ -z "$frame" ]; then | |
| echo "$(basename "$f"): couldn't resolve a source location for the crash, failing to be safe." | |
| hitOurCode=1 | |
| elif grep -q 'third_party/' <<< "$frame"; then | |
| echo "$(basename "$f"): crash is inside a vendored dependency, not our own code:" | |
| echo " $frame" | |
| else | |
| echo "$(basename "$f"): crash is inside our own code:" | |
| echo " $frame" | |
| hitOurCode=1 | |
| fi | |
| done | |
| if [ "$hitOurCode" -eq 1 ]; then | |
| exit 1 | |
| fi | |
| echo "Every crash found only implicates a vendored dependency; not failing CI for it." | |
| - name: Save the fuzz corpus even if tiff_fuzz found a crasher | |
| if: always() | |
| uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | |
| with: | |
| path: /tmp/fuzz-corpus | |
| key: tiff-fuzz-corpus-${{ github.run_id }} |