From 96ae86cb8c17ea456073810f461240e78942b31f Mon Sep 17 00:00:00 2001 From: Dominik Loidolt <dominik.loidolt@univie.ac.at> Date: Fri, 19 Apr 2024 12:29:54 +0200 Subject: [PATCH] Add github action to enable batch fuzzing with ClusterFuzzLite Batch fuzzing enables continuous, regular fuzzing on your latest HEAD and allows a corpus of inputs to build up over time, which greatly improves the effectiveness of fuzzing. Batch fuzzing can be run on a cron schedule. --- .clusterfuzzlite/build.sh | 5 +--- .github/workflows/cflite_batch.yml | 37 ++++++++++++++++++++++++++++++ test/fuzz/download_corpus.sh | 33 ++++++++++++++++++++++++++ test/fuzz/fuzz_compression.c | 1 + test/fuzz/fuzz_round_trip.c | 1 + test/fuzz/meson.build | 10 ++++---- 6 files changed, 77 insertions(+), 10 deletions(-) create mode 100644 .github/workflows/cflite_batch.yml create mode 100755 test/fuzz/download_corpus.sh diff --git a/.clusterfuzzlite/build.sh b/.clusterfuzzlite/build.sh index ed72b56..43f11d8 100755 --- a/.clusterfuzzlite/build.sh +++ b/.clusterfuzzlite/build.sh @@ -6,8 +6,6 @@ BUILD=$WORK/build rm -rf "$BUILD" mkdir -p "$BUILD" -echo "$LIB_FUZZING_ENGINE" - # setup project meson setup "$BUILD" \ --buildtype=plain \ @@ -15,8 +13,7 @@ meson setup "$BUILD" \ -Dfuzzer_ldflags="$LIB_FUZZING_ENGINE" \ -Ddebug_level=0 \ -Ddefault_library=static \ - -Db_lundef=false \ - --wrap-mode=nodownload + -Db_lundef=false # build fuzzers ninja -v -C "$BUILD" test/fuzz/fuzz_{round_trip,compression} diff --git a/.github/workflows/cflite_batch.yml b/.github/workflows/cflite_batch.yml new file mode 100644 index 0000000..30a6b53 --- /dev/null +++ b/.github/workflows/cflite_batch.yml @@ -0,0 +1,37 @@ +name: ClusterFuzzLite batch fuzzing +on: + schedule: + - cron: '0 0/6 * * *' # Every 6th hour. Change this to whatever is suitable. + push: + branches: test_batch_fuzzing + +permissions: read-all +jobs: + BatchFuzzing: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + sanitizer: + - address + - undefined + - memory + steps: + - name: Build Fuzzers (${{ matrix.sanitizer }}) + id: build + uses: google/clusterfuzzlite/actions/build_fuzzers@v1 + with: + language: c + sanitizer: ${{ matrix.sanitizer }} + - name: Run Fuzzers (${{ matrix.sanitizer }}) + id: run + uses: google/clusterfuzzlite/actions/run_fuzzers@v1 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + fuzz-seconds: 3600 + mode: 'batch' + sanitizer: ${{ matrix.sanitizer }} + output-sarif: true + storage-repo: https://gh-action:${{ secrets.ACCESS_TOKEN_GITLAB_UNI }}@gitlab.phaidra.org/loidoltd15/cmp_tool_storage.git + storage-repo-branch: main + storage-repo-branch-coverage: gh-pages diff --git a/test/fuzz/download_corpus.sh b/test/fuzz/download_corpus.sh new file mode 100755 index 0000000..5b11d24 --- /dev/null +++ b/test/fuzz/download_corpus.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +bin_path=${1:-} +if [[ -z "$bin_path" ]]; then + echo "usage: $0 <fuzz target>" + exit 1 +fi + +if [[ "$OSTYPE" == "darwin"* ]]; then + DOWNLOAD="curl --silent -L -o" +else + DOWNLOAD="wget --quiet --output-document" +fi + +ROOT_DIR=${MESON_BUILD_ROOT:-.}"/" +CORPORA_URL=https://gitlab.phaidra.org/loidoltd15/cmp_tool_storage/-/archive/main/cmp_tool_storage-main.tar.bz2 +CORPUS_COMPRESSED=$ROOT_DIR"cmp_tool_storage.tar.bz2" +CORPUS="cmp_tool_storage-main" + +CORPUS_ID=$(git ls-remote https://gitlab.phaidra.org/loidoltd15/cmp_tool_storage.git HEAD | head -c 8) +CORPUS_DIR="$ROOT_DIR"corpus__"$CORPUS_ID" + +if [ ! -d "$CORPUS_DIR" ]; then + $DOWNLOAD "$CORPUS_COMPRESSED" "$CORPORA_URL" + mkdir "$CORPUS_DIR" + tar -xf "$CORPUS_COMPRESSED" -C "$CORPUS_DIR" +fi +rm -f "$CORPUS_COMPRESSED" + +for arg in "$@" +do + find "$CORPUS_DIR"/"$CORPUS"/corpus -name "$arg" +done diff --git a/test/fuzz/fuzz_compression.c b/test/fuzz/fuzz_compression.c index 64d92ba..1c91d34 100644 --- a/test/fuzz/fuzz_compression.c +++ b/test/fuzz/fuzz_compression.c @@ -95,6 +95,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size) case CMP_ERROR_PAR_SPECIFIC: case CMP_ERROR_PAR_BUFFERS: case CMP_ERROR_PAR_MAX_USED_BITS: + case CMP_ERROR_PAR_NULL: /* chunk errors */ case CMP_ERROR_CHUNK_NULL: case CMP_ERROR_CHUNK_TOO_LARGE: diff --git a/test/fuzz/fuzz_round_trip.c b/test/fuzz/fuzz_round_trip.c index 0a6a1a7..4ee9bd8 100644 --- a/test/fuzz/fuzz_round_trip.c +++ b/test/fuzz/fuzz_round_trip.c @@ -175,6 +175,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size) case CMP_ERROR_PAR_SPECIFIC: case CMP_ERROR_PAR_BUFFERS: case CMP_ERROR_PAR_MAX_USED_BITS: + case CMP_ERROR_PAR_NULL: /* chunk errors */ case CMP_ERROR_CHUNK_NULL: case CMP_ERROR_CHUNK_TOO_LARGE: diff --git a/test/fuzz/meson.build b/test/fuzz/meson.build index 02a8ebe..f7da779 100644 --- a/test/fuzz/meson.build +++ b/test/fuzz/meson.build @@ -7,8 +7,6 @@ fuzz_targets = ['fuzz_compression.c', 'fuzz_round_trip.c'] add_languages('cpp', native: false) # libFuzzingEngine needs c++ - - foreach target : fuzz_targets file_name = target target_name = file_name.split('.').get(0) @@ -21,11 +19,11 @@ foreach target : fuzz_targets link_language : 'cpp' # libFuzzingEngine needs c++ ) -# todo add seed corpus + corpus_path = run_command('./download_corpus.sh', target_name, check: true).stdout().split() + test(target_name + ' 10 min', fuzz_exe, - args : ['-rss_limit_mb=2560', '-timeout=25', '-max_total_time=600'], - # args : ['-timeout=25', '-max_total_time=600'], + args : ['-rss_limit_mb=2560', '-timeout=25', '-max_total_time=600', corpus_path], env : test_env, is_parallel : false, # suite : 'fuzzing', @@ -34,7 +32,7 @@ foreach target : fuzz_targets test(target_name + ' non stop', fuzz_exe, - args : ['-rss_limit_mb=2560', '-timeout=25'], + args : ['-rss_limit_mb=2560', '-timeout=25', corpus_path], env : test_env, is_parallel : false, # suite : 'fuzzing', -- GitLab