name: Fuzz and run valgrind on: push: branches: - master pull_request: branches: - master schedule: - cron: 23 */8 * * * jobs: build: if: >- ! contains(toJSON(github.event.commits.*.message), '[skip ci]') && ! contains(toJSON(github.event.commits.*.message), '[skip github]') runs-on: ubuntu-latest env: # fuzzers that change behaviour with SIMDJSON_FORCE_IMPLEMENTATION defaultimplfuzzers: atpointer dump dump_raw_tape element minify parser print_json # fuzzers that loop over the implementations themselves, or don't need to switch. implfuzzers: implementations minifyimpl ndjson ondemand padded utf8 implementations: haswell westmere fallback UBSAN_OPTIONS: halt_on_error=1 MAXLEN: -max_len=4000 CLANGVERSION: 19 # which optimization level to use for the sanitizer build (see build_fuzzer.variants.sh) OPTLEVEL: -O3 steps: - name: Install packages necessary for building run: | sudo apt update sudo apt-get install --quiet ninja-build valgrind zip unzip lsb-release wget software-properties-common gnupg wget https://apt.llvm.org/llvm.sh sudo apt-get purge --auto-remove llvm python3-lldb-15 llvm-15 chmod +x llvm.sh sudo ./llvm.sh $CLANGVERSION - uses: actions/checkout@v4 - uses: actions/cache@v4 with: path: dependencies/.cache key: ${{ hashFiles('dependencies/CMakeLists.txt') }} - uses: actions/cache@v4 id: cache-corpus with: path: out/ key: corpus-${{ github.run_id }} restore-keys: corpus- - name: show statistics for the cached corpus run: | echo number of files in github action corpus cache: find out -type f |wc -l - name: Create and prepare the initial seed corpus run: | fuzz/build_corpus.sh mv corpus.zip seed_corpus.zip mkdir seedcorpus unzip -q -d seedcorpus seed_corpus.zip - name: List clang versions run: | ls /usr/bin/clang* which clang++ clang++ --version - name: Build all the variants run: CLANGSUFFIX=-$CLANGVERSION fuzz/build_fuzzer_variants.sh - name: Explore fast (release build, default implementation) run: | set -eux for fuzzer in $defaultimplfuzzers $implfuzzers; do mkdir -p out/$fuzzer # in case this is a new fuzzer, or the github action cached corpus is broken # get input from everyone else (corpus cross pollination) others=$(find out -type d -not -name $fuzzer -not -name out -not -name cmin) build-fast/fuzz/fuzz_$fuzzer out/$fuzzer $others seedcorpus -max_total_time=30 $MAXLEN done - name: Fuzz default impl. fuzzers with sanitizer+asserts (good at detecting errors) run: | set -eux for fuzzer in $defaultimplfuzzers; do # get input from everyone else (corpus cross pollination) others=$(find out -type d -not -name $fuzzer -not -name out -not -name cmin) for implementation in $implementations; do export SIMDJSON_FORCE_IMPLEMENTATION=$implementation build-sanitizers$OPTLEVEL/fuzz/fuzz_$fuzzer out/$fuzzer $others seedcorpus -max_total_time=20 $MAXLEN done echo now have $(ls out/$fuzzer |wc -l) files in corpus done - name: Fuzz differential impl. fuzzers with sanitizer+asserts (good at detecting errors) run: | set -eux for fuzzer in $implfuzzers; do # get input from everyone else (corpus cross pollination) others=$(find out -type d -not -name $fuzzer -not -name out -not -name cmin) build-sanitizers$OPTLEVEL/fuzz/fuzz_$fuzzer out/$fuzzer $others seedcorpus -max_total_time=20 $MAXLEN echo now have $(ls out/$fuzzer |wc -l) files in corpus done - name: Minimize the corpus with the fast fuzzer on the default implementation run: | set -eux for fuzzer in $defaultimplfuzzers $implfuzzers; do mkdir -p out/cmin/$fuzzer # get input from everyone else (corpus cross pollination) others=$(find out -type d -not -name $fuzzer -not -name out -not -name cmin) build-fast/fuzz/fuzz_$fuzzer -merge=1 $MAXLEN out/cmin/$fuzzer out/$fuzzer $others seedcorpus rm -rf out/$fuzzer mv out/cmin/$fuzzer out/$fuzzer done - name: Package the corpus into an artifact run: | for fuzzer in $defaultimplfuzzers $implfuzzers; do tar rf corpus.tar out/$fuzzer done - name: Save the corpus as a github artifact uses: actions/upload-artifact@v4 with: name: corpus path: corpus.tar - name: Store the corpus externally run: | gzip --keep corpus.tar curl -F"filedata=@corpus.tar.gz" https://simdjson:${{ secrets.fuzzdatapassword }}@www.pauldreik.se/fuzzdata/index.php # This takes a subset of the minimized corpus and run it through valgrind. It is slow, # therefore take a "random" subset. The random selection is accomplished by sorting on filenames, # which are hashes of the content. - name: Run some of the minimized corpus through valgrind (replay build, default implementation) run: | for fuzzer in $defaultimplfuzzers $implfuzzers; do find out/$fuzzer -type f |sort|head -n200|xargs -n40 valgrind build-replay/fuzz/fuzz_$fuzzer 2>&1|tee valgrind-$fuzzer.txt done - name: Compress the valgrind output run: tar cf valgrind.tar valgrind-*.txt - name: Save valgrind output as a github artifact uses: actions/upload-artifact@v4 if: always() with: name: valgrindresults path: valgrind.tar if-no-files-found: ignore - name: Archive any crashes as an artifact uses: actions/upload-artifact@v4 if: always() with: name: crashes path: | crash-* leak-* timeout-* if-no-files-found: ignore