diff --git a/.github/workflows/nightly-fuzzing.yml b/.github/workflows/nightly-fuzzing.yml new file mode 100644 index 0000000000..c97dd60616 --- /dev/null +++ b/.github/workflows/nightly-fuzzing.yml @@ -0,0 +1,161 @@ +name: Fuzz Testing Nightly +on: + schedule: + # 3:00 AM PST monday-saturday + - cron: '00 11 * * 1-6' + +jobs: + image_verify_seed_corpus: + name: Build Image Verifier seed corpus + runs-on: ubuntu-22.04 + + env: + # Change this to a new random value if you suspect the cache is corrupted + CACHE_BUSTER: 6542f37bb328 + + steps: + - name: Checkout repo + uses: actions/checkout@v3 + with: + submodules: 'true' + + - name: Restore seed corpus + uses: actions/cache/restore@v3 + id: image_bundle_restore + with: + path: ./image/verify/fuzz/common_corpus/ + key: image_verify-common_corpus-${{ env.CACHE_BUSTER }} + + - name: Build seed corpus + if: steps.image_bundle_restore.outputs.cache-hit != 'true' + run: | + mkdir -p image/verify/fuzz/common_corpus + for x in $(seq 01 04); do + cargo run -j$(nproc) --manifest-path=builder/Cargo.toml --release --bin image -- --rom /dev/null --fw image/verify/fuzz/common_corpus/${x}; \ + cargo clean; \ + done + + - name: Save seed corpus + uses: actions/cache/save@v3 + if: steps.image_bundle_restore.outputs.cache-hit != 'true' + with: + path: ./image/verify/fuzz/common_corpus/ + key: image_verify-common_corpus-${{ env.CACHE_BUSTER }} + + dpe_libfuzzer: + uses: ./.github/workflows/reusable-libfuzzer.yml + with: + name: dpe + fuzz_target_path: dpe/dpe/fuzz + fuzz_target_name: fuzz_target_1 + fuzz_target_max_len: 512 + fuzzer_features: + fuzzer_sanitiser: address + + dpe_afl: + uses: ./.github/workflows/reusable-aflplusplus.yml + with: + name: dpe + fuzz_target_path: dpe/dpe/fuzz + fuzz_target_name: fuzz_target_1 + fuzz_target_max_len: 512 + fuzzer_features: + + image_verify_libfuzzer_unstructured: + needs: image_verify_seed_corpus + uses: ./.github/workflows/reusable-libfuzzer.yml + with: + name: image_verify + fuzz_target_path: image/verify/fuzz + fuzz_target_name: fuzz_target_coldreset + fuzz_target_max_len: 131072 + fuzzer_features: + fuzzer_sanitiser: address + + image_verify_libfuzzer_structured: + needs: image_verify_seed_corpus + uses: ./.github/workflows/reusable-libfuzzer.yml + with: + name: image_verify + fuzz_target_path: image/verify/fuzz + fuzz_target_name: fuzz_target_coldreset + fuzz_target_max_len: 131072 + fuzzer_features: struct-aware + fuzzer_sanitiser: address + + image_verify_afl_unstructured: + needs: image_verify_seed_corpus + uses: ./.github/workflows/reusable-aflplusplus.yml + with: + name: image_verify + fuzz_target_path: image/verify/fuzz + fuzz_target_name: fuzz_target_coldreset + fuzz_target_max_len: 131072 + fuzzer_features: + + image_verify_afl_structured: + needs: image_verify_seed_corpus + uses: ./.github/workflows/reusable-aflplusplus.yml + with: + name: image_verify + fuzz_target_path: image/verify/fuzz + fuzz_target_name: fuzz_target_coldreset + fuzz_target_max_len: 131072 + fuzzer_features: struct-aware + + lms_libfuzzer_unstructured: + uses: ./.github/workflows/reusable-libfuzzer.yml + with: + name: lms + fuzz_target_path: drivers/fuzz + fuzz_target_name: fuzz_target_lms + fuzz_target_max_len: 16384 + fuzzer_features: + fuzzer_sanitiser: address + + lms_libfuzzer_structured: + uses: ./.github/workflows/reusable-libfuzzer.yml + with: + name: lms + fuzz_target_path: drivers/fuzz + fuzz_target_name: fuzz_target_lms + fuzz_target_max_len: 16384 + fuzzer_features: struct-aware + fuzzer_sanitiser: address + + lms_afl_unstructured: + uses: ./.github/workflows/reusable-aflplusplus.yml + with: + name: lms + fuzz_target_path: drivers/fuzz + fuzz_target_name: fuzz_target_lms + fuzz_target_max_len: 16384 + fuzzer_features: + + lms_afl_structured: + uses: ./.github/workflows/reusable-aflplusplus.yml + with: + name: lms + fuzz_target_path: drivers/fuzz + fuzz_target_name: fuzz_target_lms + fuzz_target_max_len: 16384 + fuzzer_features: struct-aware + + x509_libfuzzer: + uses: ./.github/workflows/reusable-libfuzzer.yml + with: + name: x509 + fuzz_target_path: x509/fuzz + fuzz_target_name: fuzz_target_1 + fuzz_target_max_len: 8192 + fuzzer_features: + fuzzer_sanitiser: address + + x509_afl: + uses: ./.github/workflows/reusable-aflplusplus.yml + with: + name: x509 + fuzz_target_path: x509/fuzz + fuzz_target_name: fuzz_target_1 + fuzz_target_max_len: 8192 + fuzzer_features: diff --git a/.github/workflows/reusable-aflplusplus.yml b/.github/workflows/reusable-aflplusplus.yml new file mode 100644 index 0000000000..c3ae517e37 --- /dev/null +++ b/.github/workflows/reusable-aflplusplus.yml @@ -0,0 +1,164 @@ +name: Reusable AFL++ +on: + workflow_call: + inputs: + name: + required: true + type: string + fuzz_target_path: + required: true + type: string + fuzz_target_name: + required: true + type: string + fuzz_target_max_len: + required: true + type: string + fuzzer_features: + required: true + type: string + +jobs: + reusable_aflplus_plus: + # TODO: Set these as parameters? + runs-on: ubuntu-22.04 + timeout-minutes: 90 + + env: + # Change this to a new random value if you suspect the cache is corrupted + CACHE_BUSTER: 6542f37bb328 + + steps: + - name: Checkout repo + uses: actions/checkout@v3 + with: + submodules: 'true' + + - name: Install dependencies + run: | + rustup toolchain install nightly-2023-04-15 + cargo +nightly-2023-04-15 install cargo-afl + sudo apt-get install gnuplot + echo core | sudo tee /proc/sys/kernel/core_pattern + + - name: Restore corpus dir + uses: actions/cache/restore@v3 + with: + path: ${{ inputs.fuzz_target_path }}/corpus/ + key: ${{ inputs.name }}-${{ env.CACHE_BUSTER }} + + - name: Run fuzzing + run: | + cd ${{ inputs.fuzz_target_path }} + mkdir -p {corpus,artifacts}/${{ inputs.fuzz_target_name }} + cp common_corpus/* corpus/${{ inputs.fuzz_target_name }}/ + # For parallelised workers + export AFL_IMPORT_FIRST=1 + # For faster CI runs + #export AFL_CMPLOG_ONLY_NEW=1 + export AFL_FAST_CAL=1 + cargo +nightly-2023-04-15 afl build \ + --features afl,${{ inputs.fuzzer_features }} + cargo +nightly-2023-04-15 afl fuzz \ + -i corpus/${{ inputs.fuzz_target_name }} \ + -o artifacts/${{ inputs.fuzz_target_name }} \ + -G ${{ inputs.fuzz_target_max_len }} \ + -V 3600 \ + -p fast -L 1 -l 2ATR \ + -M node01-cmplog_en \ + target/debug/${{ inputs.fuzz_target_name }} & + for x in $(seq 3 4 $(nproc)); do \ + cargo +nightly-2023-04-15 afl fuzz \ + -i corpus/${{ inputs.fuzz_target_name }} \ + -o artifacts/${{ inputs.fuzz_target_name }} \ + -G ${{ inputs.fuzz_target_max_len }} \ + -V 3600 \ + -p fast -L 0 -c - \ + -S node$(( $x ))-fast-mopt_en \ + target/debug/${{ inputs.fuzz_target_name }} > /dev/null & ; \ + cargo +nightly-2023-04-15 afl fuzz \ + -i corpus/${{ inputs.fuzz_target_name }} \ + -o artifacts/${{ inputs.fuzz_target_name }} \ + -G ${{ inputs.fuzz_target_max_len }} \ + -V 3600 \ + -p fast -c - \ + -S node$(( $x + 1 ))-fast-mopt_dis \ + target/debug/${{ inputs.fuzz_target_name }} > /dev/null & ; \ + cargo +nightly-2023-04-15 afl fuzz \ + -i corpus/${{ inputs.fuzz_target_name }} \ + -o artifacts/${{ inputs.fuzz_target_name }} \ + -G ${{ inputs.fuzz_target_max_len }} \ + -V 3600 \ + -p explore -L 0 -c - \ + -S node$(( $x + 2 ))-explore-mopt_en \ + target/debug/${{ inputs.fuzz_target_name }} > /dev/null & ; \ + cargo +nightly-2023-04-15 afl fuzz \ + -i corpus/${{ inputs.fuzz_target_name }} \ + -o artifacts/${{ inputs.fuzz_target_name }} \ + -G ${{ inputs.fuzz_target_max_len }} \ + -V 3600 \ + -p explore -c - \ + -S node$(( $x + 3))-explore-mopt_dis \ + target/debug/${{ inputs.fuzz_target_name }} > /dev/null & ; \ + done + # FIXME: Step detection for putting tasks in background + cargo +nightly-2023-04-15 afl fuzz \ + -i corpus/${{ inputs.fuzz_target_name }} \ + -o artifacts/${{ inputs.fuzz_target_name }} \ + -G ${{ inputs.fuzz_target_max_len }} \ + -V 3600 \ + -p fast -L 1 -c - \ + -S node02-cmplog_dis \ + target/debug/${{ inputs.fuzz_target_name }} > /dev/null + cargo +nightly-2023-04-15 afl whatsup -s -d artifacts + + - name: Export coverage + run: | + cd ${{ inputs.fuzz_target_path }} + cargo +nightly-2023-04-15 afl showmap \ + -C -i artifacts/default -o /dev/null \ + -- \ + target/debug/${{inputs.fuzz_target_name }} + cargo +nightly-2023-04-15 afl plot artifacts/default plot + + - name: Merge corpus between runs + run: | + cd ${{ inputs.fuzz_target_path }} + mkdir new_corpus + cargo +nightly-2023-04-15 afl cmin \ + -i corpus \ + -o new_corpus \ + -T all \ + -- \ + target/debug/${{ inputs.fuzz_target_name }} + rm -rf corpus && mv new_corpus corpus + + - name: Attempt to minimise each crash for test cases + run: | + cd ${{ inputs.fuzz_target_path }} + mkdir test_cases + for crashing_input in artifacts/${{ inputs.fuzz_target_name }}/*; do + cargo +nightly-2023-04-15 afl tmin \ + -i $crashing_input \ + -o test_cases/$(basename $crashing_input) + -- \ + target/debug/${{ inputs.fuzz_target_name }} \ + done + + - name: Save corpus dir + uses: actions/cache/save@v3 + with: + path: ${{ inputs.fuzz_target_path }}/corpus/ + key: ${{ inputs.name }}-${{ env.CACHE_BUSTER }} + + - name: Archive test cases dir + uses: actions/upload-artifact@v3 + with: + name: test_cases + path: ${{ inputs.fuzz_target_path }}/test_cases/ + + - name: Archive fuzzing coverage + uses: actions/upload-artifact@v3 + with: + name: plot + path: ${{ inputs.fuzz_target_path }}/plot/ diff --git a/.github/workflows/reusable-libfuzzer.yml b/.github/workflows/reusable-libfuzzer.yml new file mode 100644 index 0000000000..e69e52b317 --- /dev/null +++ b/.github/workflows/reusable-libfuzzer.yml @@ -0,0 +1,127 @@ +name: Reusable libFuzzer +on: + workflow_call: + inputs: + name: + required: true + type: string + fuzz_target_path: + required: true + type: string + fuzz_target_name: + required: true + type: string + fuzz_target_max_len: + required: true + type: string + fuzzer_features: + required: true + type: string + fuzzer_sanitiser: + required: true + type: string + +jobs: + reusable_libfuzzer: + # TODO: Set these as parameters? + runs-on: ubuntu-22.04 + timeout-minutes: 90 + + env: + # Change this to a new random value if you suspect the cache is corrupted + CACHE_BUSTER: 6542f37bb328 + + steps: + - name: Checkout repo + uses: actions/checkout@v3 + with: + submodules: 'true' + + - name: Install dependencies + run: | + rustup toolchain install nightly-2023-04-15 + rustup component add --toolchain nightly-2023-04-15 llvm-tools + cargo +nightly-2023-04-15 install cargo-fuzz + + - name: Restore corpus dir + uses: actions/cache/restore@v3 + with: + path: ${{ inputs.fuzz_target_path }}/corpus/ + key: ${{ inputs.name }}-${{ env.CACHE_BUSTER }} + + - name: Run fuzzing + run: | + cd ${{ inputs.fuzz_target_path }} + mkdir -p {corpus,artifacts}/${{ inputs.fuzz_target_name }} + cargo +nightly-2023-04-15 fuzz run \ + --features libfuzzer-sys,${{ inputs.fuzzer_features }} \ + -s ${{ inputs.fuzzer_sanitiser }} \ + ${{ inputs.fuzz_target_name }} \ + corpus/${{ inputs.fuzz_target_name }} common_corpus \ + -- \ + -max_len=${{ inputs.fuzz_target_max_len }} \ + -max_total_time=3600 \ + -jobs=480 -workers=$(($(nproc) / 2)) + + - name: Export coverage + run: | + cd ${{ inputs.fuzz_target_path }} + cargo +nightly-2023-04-15 fuzz coverage \ + --features libfuzzer-sys,${{ inputs.fuzzer_features }} \ + -s ${{ inputs.fuzzer_sanitiser }} \ + ${{ inputs.fuzz_target_name }} \ + corpus/${{ inputs.fuzz_target_name }} common_corpus \ + -- \ + -max_len=${{ inputs.fuzz_target_max_len }} + ~/.rustup/toolchains/nightly-2023-04-15-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/bin/llvm-cov show \ + target/x86_64-unknown-linux-gnu/coverage/x86_64-unknown-linux-gnu/release/${{ inputs.fuzz_target_name }} \ + --format=html \ + -instr-profile=coverage/${{ inputs.fuzz_target_name }}/coverage.profdata \ + > index.html + + - name: Merge corpus between runs + run: | + cd ${{ inputs.fuzz_target_path }} + mkdir new_corpus + cargo +nightly-2023-04-15 fuzz cmin \ + --features libfuzzer-sys,${{ inputs.fuzzer_features }} \ + -s ${{ inputs.fuzzer_sanitiser }} \ + ${{ inputs.fuzz_target_name }} \ + new_corpus corpus \ + -- \ + -max_len=${{ inputs.fuzz_target_max_len }} \ + -jobs=480 -workers=$(($(nproc) / 2)) \ + rm -rf corpus && mv new_corpus corpus + + - name: Attempt to minimise each crash for test cases + run: | + cd ${{ inputs.fuzz_target_path }} + mkdir test_cases + for crashing_input in artifacts/${{ inputs.fuzz_target_name }}/*; do + cargo +nightly-2023-04-15 fuzz tmin \ + --features libfuzzer-sys,${{ inputs.fuzzer_features }} \ + -s ${{ inputs.fuzzer_sanitiser }} \ + ${{ inputs.fuzz_target_name }} \ + $crashing_input \ + -- \ + -max_len=${{ inputs.fuzz_target_max_len }} \ + done + mv artifacts/${{ inputs.fuzz_target_name }}/minimized-from-* test_cases/ + + - name: Save corpus dir + uses: actions/cache/save@v3 + with: + path: ${{ inputs.fuzz_target_path }}/corpus/ + key: ${{ inputs.name }}-${{ env.CACHE_BUSTER }} + + - name: Archive test cases dir + uses: actions/upload-artifact@v3 + with: + name: test_cases + path: ${{ inputs.fuzz_target_path }}/test_cases/ + + - name: Archive fuzzing coverage + uses: actions/upload-artifact@v3 + with: + name: coverage + path: ${{ inputs.fuzz_target_path }}/index.html