-
Notifications
You must be signed in to change notification settings - Fork 5.5k
[tools] initial fuzz coverage script #10289
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
fd15170
83cb80b
540dd43
616a834
45411d0
8ea16bf
ae48280
48f51ed
de89135
e60cc43
bf70062
73ddc72
97b3b41
e8b0cf2
42a9e20
178d684
d14e72a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,129 @@ | ||
| #!/bin/bash | ||
|
|
||
| set -e | ||
|
|
||
| [[ -z "${SRCDIR}" ]] && SRCDIR="${PWD}" | ||
|
|
||
| echo "Starting run_envoy_fuzz_coverage.sh..." | ||
| echo " PWD=$(pwd)" | ||
| echo " SRCDIR=${SRCDIR}" | ||
|
|
||
| # This is the fuzz target that will be run to generate coverage data. | ||
| if [[ $# -gt 0 ]]; then | ||
| FUZZ_TARGETS=$* | ||
| else | ||
| # By default, this will be all fuzz targets. | ||
| FUZZER_TARGETS_CC=$(find . -name *_fuzz_test.cc) | ||
| FUZZER_TARGETS="$(for t in ${FUZZER_TARGETS_CC}; do echo "${t:2:-3}"; done)" | ||
| FUZZ_TARGETS="" | ||
| for t in ${FUZZER_TARGETS} | ||
| do | ||
| FUZZ_TARGETS+="//"$(dirname "$t")":"$(basename "$t")" " | ||
| done | ||
| fi | ||
|
|
||
| echo ${FUZZ_TARGETS} | ||
|
|
||
| # Build all fuzz targets to run instrumented with libfuzzer in sequence. | ||
| echo "Building fuzz targets..." | ||
| for t in ${FUZZ_TARGETS} | ||
| do | ||
| bazel build "${t}_with_libfuzzer" --config asan-fuzzer -c opt | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you do a single
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
| done | ||
|
|
||
| # Now run each fuzz target in parallel for 60 seconds. | ||
| pids="" | ||
| TEMP_CORPORA="" | ||
| echo "Running fuzz targets..." | ||
| for t in ${FUZZ_TARGETS} | ||
| do | ||
| # Get the original corpus for the fuzz target | ||
| ORIGINAL_CORPUS=$(bazel query "labels(srcs, ${t}_corpus)" | head -1) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this work for a generated corpus?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, modified to pull data attribute, so now it works for something like route fuzz test |
||
| ORIGINAL_CORPUS=${ORIGINAL_CORPUS/://} | ||
| ORIGINAL_CORPUS=$(dirname ${ORIGINAL_CORPUS}) | ||
|
asraa marked this conversation as resolved.
Outdated
|
||
| # Create temp directory in target's corpus | ||
| CORPUS_DIR=$(mktemp -d -p $(pwd)/${ORIGINAL_CORPUS:2}) | ||
| TEMP_CORPORA+="${CORPUS_DIR} " | ||
| # Run fuzzing process. | ||
| TARGET_BINARY="${t/://}" | ||
| bazel-bin/${TARGET_BINARY:2}_with_libfuzzer -max_total_time=60 ${CORPUS_DIR} $(pwd)${ORIGINAL_CORPUS:1} & | ||
|
asraa marked this conversation as resolved.
Outdated
|
||
| pids="$pids $!" | ||
| done | ||
|
|
||
| # Wait for background process to run. | ||
| # TODO? Processes will still run in background if user ctrl-c. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider adding trap handlers, see the |
||
| for pid in $pids; do | ||
| wait ${pid} | ||
| if [ $? -eq 0 ]; then | ||
| echo "SUCCESS for $pid" | ||
| else | ||
| echo "FAILED for $pid" | ||
| fi | ||
| done | ||
|
|
||
| # This will be used by llvm-cov and points to the binaries. | ||
| OBJECTS="" | ||
|
|
||
| # TODO can I re-use cached targets from bazel build and bazel coverage? | ||
|
asraa marked this conversation as resolved.
Outdated
|
||
| echo "Running bazel coverage for each target..." | ||
| for t in ${FUZZ_TARGETS} | ||
|
asraa marked this conversation as resolved.
Outdated
|
||
| do | ||
| # TODO can I also grab the corpus more easily. ${t}_corpus doesn't always work for cloudesf etc | ||
| ORIGINAL_CORPUS=$(bazel query "labels(srcs, ${t}_corpus)" | head -1) | ||
| ORIGINAL_CORPUS=${ORIGINAL_CORPUS/://} | ||
| ORIGINAL_CORPUS=$(dirname ${ORIGINAL_CORPUS}) | ||
| BAZEL_USE_LLVM_NATIVE_COVERAGE=1 GCOV=llvm-profdata bazel coverage ${BAZEL_BUILD_OPTIONS} \ | ||
| --instrumentation_filter=//source/...,//include/... \ | ||
| --test_timeout=2000 --cxxopt="-DENVOY_CONFIG_COVERAGE=1" --test_output=streamed \ | ||
| --test_env=HEAPCHECK= "${t}_with_libfuzzer" --test_arg=$(pwd)${ORIGINAL_CORPUS:1} --test_arg=-runs=0 | ||
| TARGET_BINARY="${t/://}" | ||
| if [[ -z $OBJECTS ]]; then | ||
| # The first object needs to be passed without -object= flag. | ||
| OBJECTS="bazel-bin/${TARGET_BINARY:2}_with_libfuzzer" | ||
| else | ||
| OBJECTS="$OBJECTS -object=bazel-bin/${TARGET_BINARY:2}_with_libfuzzer" | ||
| fi | ||
| done | ||
|
|
||
| COVERAGE_DIR="${SRCDIR}"/generated/coverage | ||
| mkdir -p "${COVERAGE_DIR}" | ||
|
|
||
| COVERAGE_IGNORE_REGEX="(/external/|pb\.(validate\.)?(h|cc)|/chromium_url/|/test/|/tmp|/source/extensions/quic_listeners/quiche/)" | ||
| COVERAGE_DATA="${COVERAGE_DIR}/coverage.dat" | ||
|
|
||
| echo "Merging coverage data..." | ||
| llvm-profdata merge -sparse -o ${COVERAGE_DATA} $(find -L bazel-out/k8-fastbuild/testlogs/test/ -name coverage.dat) | ||
|
|
||
| echo "Generating report..." | ||
| llvm-cov show ${OBJECTS} -instr-profile="${COVERAGE_DATA}" -Xdemangler=c++filt \ | ||
| -ignore-filename-regex="${COVERAGE_IGNORE_REGEX}" -output-dir=${COVERAGE_DIR} -format=html | ||
| sed -i -e 's|>proc/self/cwd/|>|g' "${COVERAGE_DIR}/index.html" | ||
| sed -i -e 's|>bazel-out/[^/]*/bin/\([^/]*\)/[^<]*/_virtual_includes/[^/]*|>\1|g' "${COVERAGE_DIR}/index.html" | ||
|
|
||
| [[ -z "${ENVOY_COVERAGE_DIR}" ]] || rsync -av "${COVERAGE_DIR}"/ "${ENVOY_COVERAGE_DIR}" | ||
|
|
||
| # Clean up... | ||
| for corpus in ${TEMP_CORPORA} | ||
| do | ||
| rm -rf $corpus | ||
| done | ||
|
|
||
|
|
||
| if [ "$VALIDATE_COVERAGE" == "true" ] | ||
| then | ||
| COVERAGE_VALUE=$(llvm-cov export "${COVERAGE_BINARY}" -instr-profile="${COVERAGE_DATA}" \ | ||
| -ignore-filename-regex="${COVERAGE_IGNORE_REGEX}" -summary-only | \ | ||
| python3 -c "import sys, json; print(json.load(sys.stdin)['data'][0]['totals']['lines']['percent'])") | ||
| COVERAGE_THRESHOLD=97.0 | ||
| COVERAGE_FAILED=$(echo "${COVERAGE_VALUE}<${COVERAGE_THRESHOLD}" | bc) | ||
| if test ${COVERAGE_FAILED} -eq 1; then | ||
| echo Code coverage ${COVERAGE_VALUE} is lower than limit of ${COVERAGE_THRESHOLD} | ||
| exit 1 | ||
| else | ||
| echo Code coverage ${COVERAGE_VALUE} is good and higher than limit of ${COVERAGE_THRESHOLD} | ||
| fi | ||
| fi | ||
| echo "HTML coverage report is in ${COVERAGE_DIR}/index.html" | ||
|
|
||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something like this also exists in the OSS-fuzz runner script. Could we define this in a common bash utility script and import it in both locations?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you think of the tag? makes it an easy bazel query