-
Notifications
You must be signed in to change notification settings - Fork 714
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
ci: run fuzz tests in parallel and generate coverage report #4960
Changes from 3 commits
88a5716
37a5205
224a017
ce73ffa
6352fe4
daab18d
e1b3a1d
4a4a322
6e12ef2
be23009
a2e4156
a5fdc9f
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 | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -13,55 +13,11 @@ | |||||||||||
# limitations under the License. | ||||||||||||
version: 0.2 | ||||||||||||
|
||||||||||||
batch: | ||||||||||||
build-matrix: | ||||||||||||
static: | ||||||||||||
env: | ||||||||||||
privileged-mode: true | ||||||||||||
dynamic: | ||||||||||||
env: | ||||||||||||
compute-type: | ||||||||||||
- BUILD_GENERAL1_LARGE | ||||||||||||
image: | ||||||||||||
- 024603541914.dkr.ecr.us-west-2.amazonaws.com/docker:ubuntu22codebuild | ||||||||||||
privileged-mode: true | ||||||||||||
variables: | ||||||||||||
S2N_LIBCRYPTO: | ||||||||||||
- awslc | ||||||||||||
FUZZ_TESTS: | ||||||||||||
- "s2n_cert_req_recv_test" | ||||||||||||
- "s2n_certificate_extensions_parse_test" | ||||||||||||
- "s2n_client_ccs_recv_test" | ||||||||||||
- "s2n_client_cert_recv_test" | ||||||||||||
- "s2n_client_cert_verify_recv_test" | ||||||||||||
- "s2n_client_finished_recv_test" | ||||||||||||
- "s2n_client_fuzz_test" | ||||||||||||
- "s2n_client_hello_recv_fuzz_test" | ||||||||||||
- "s2n_client_key_recv_fuzz_test" | ||||||||||||
- "s2n_deserialize_resumption_state_test" | ||||||||||||
- "s2n_encrypted_extensions_recv_test" | ||||||||||||
- "s2n_extensions_client_key_share_recv_test" | ||||||||||||
- "s2n_extensions_client_supported_versions_recv_test" | ||||||||||||
- "s2n_extensions_server_key_share_recv_test" | ||||||||||||
- "s2n_extensions_server_supported_versions_recv_test" | ||||||||||||
- "s2n_hybrid_ecdhe_kyber_r3_fuzz_test" | ||||||||||||
- "s2n_kyber_r3_recv_ciphertext_fuzz_test" | ||||||||||||
- "s2n_kyber_r3_recv_public_key_fuzz_test" | ||||||||||||
- "s2n_memory_leak_negative_test" | ||||||||||||
- "s2n_openssl_diff_pem_parsing_test" | ||||||||||||
- "s2n_recv_client_supported_groups_test" | ||||||||||||
- "s2n_select_server_cert_test" | ||||||||||||
- "s2n_server_ccs_recv_test" | ||||||||||||
- "s2n_server_cert_recv_test" | ||||||||||||
- "s2n_server_extensions_recv_test" | ||||||||||||
- "s2n_server_finished_recv_test" | ||||||||||||
- "s2n_server_fuzz_test" | ||||||||||||
- "s2n_server_hello_recv_test" | ||||||||||||
- "s2n_stuffer_pem_fuzz_test" | ||||||||||||
- "s2n_tls13_cert_req_recv_test" | ||||||||||||
- "s2n_tls13_cert_verify_recv_test" | ||||||||||||
- "s2n_tls13_client_finished_recv_test" | ||||||||||||
- "s2n_tls13_server_finished_recv_test" | ||||||||||||
env: | ||||||||||||
variables: | ||||||||||||
CC: "/usr/bin/clang" | ||||||||||||
jmayclin marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||
CXX: "/usr/bin/clang++" | ||||||||||||
S2N_LIBCRYPTO: "awslc" | ||||||||||||
|
||||||||||||
phases: | ||||||||||||
pre_build: | ||||||||||||
|
@@ -77,12 +33,20 @@ phases: | |||||||||||
cmake . -Bbuild \ | ||||||||||||
-DCMAKE_PREFIX_PATH=/usr/local/$S2N_LIBCRYPTO \ | ||||||||||||
-DS2N_FUZZ_TEST=on \ | ||||||||||||
-DFUZZ_TIMEOUT_SEC=27000 | ||||||||||||
-DFUZZ_TIMEOUT_SEC=27000 \ | ||||||||||||
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. Making this an environment variable makes it easy to over-ride later. 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. Agreed. I had to make changes to this line to test for shorter duration. Will change this to an env var |
||||||||||||
-DCOVERAGE=on \ | ||||||||||||
-DBUILD_SHARED_LIBS=ON | ||||||||||||
- cmake --build ./build -- -j $(nproc) | ||||||||||||
post_build: | ||||||||||||
on-failure: ABORT | ||||||||||||
commands: | ||||||||||||
# -L: Restrict tests to labels matching the pattern 'fuzz' | ||||||||||||
# -R: Run the single fuzz test defined in ${FUZZ_TESTS} | ||||||||||||
# --timeout: override ctest's default timeout of 1500 | ||||||||||||
- cmake --build build/ --target test -- ARGS="-L fuzz -R ${FUZZ_TESTS} --output-on-failure --timeout 28800" | ||||||||||||
- cmake --build build/ --target test -- ARGS="-L fuzz --output-on-failure -j $(nproc) --timeout 28800" | ||||||||||||
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. same as above; if timeout is an env. var we can over-ride it later 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. Hmm I suspect we won't be running fuzz tests for more than 28800 secs though. I'm also not exactly sure how I make this into env, probably something like this?
|
||||||||||||
- ./tests/fuzz/calcTotalCov.sh | ||||||||||||
|
||||||||||||
artifacts: | ||||||||||||
# upload all files in the fuzz_coverage_report directory | ||||||||||||
files: | ||||||||||||
- '**/*' | ||||||||||||
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. The broad file mask should keep this from failing, but have you tested the use of artifact uploads? (they have been flaky). 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. This wildcard match looks a little bit suspicious to me. Won't this upload everything? I'd expect the pattern to be something more like 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.
documentation: https://docs.aws.amazon.com/codebuild/latest/userguide/build-spec-ref.html#:~:text=artifacts/base%2Ddirectory Alternatively I could probably just specify s2n-tls/codebuild/spec/buildspec_unit_coverage.yml Lines 41 to 45 in aaae641
Either way, I will test this to confirm the behavior, and paste the result. 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. Ahh, I just missed the "base-directory" field 🤦 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. Tested uploading coverage files to S3 bucket and is working. I uploaded a folder |
||||||||||||
base-directory: fuzz_coverage_report |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,36 +24,23 @@ if [ "$#" -ne "0" ]; then | |
usage | ||
fi | ||
|
||
if [[ -z "$S2N_ROOT" ]]; then | ||
S2N_ROOT=../.. | ||
fi | ||
|
||
FUZZCOV_SOURCES="${S2N_ROOT}/api ${S2N_ROOT}/bin ${S2N_ROOT}/crypto ${S2N_ROOT}/error ${S2N_ROOT}/stuffer ${S2N_ROOT}/tls ${S2N_ROOT}/utils" | ||
FUZZCOV_SOURCES="api bin crypto error stuffer tls utils" | ||
|
||
printf "Calculating total s2n coverage... \n" | ||
|
||
# Outputs fuzz coverage results if the FUZZ_COVERAGE environment variable is set | ||
# Total coverage is overlayed on source code in s2n_cov.html and coverage statistics are available in s2n_cov.txt | ||
# If using LLVM version 9 or greater, coverage is output in LCOV format instead of HTML | ||
# All files are stored in the s2n coverage directory | ||
if [[ "$FUZZ_COVERAGE" == "true" ]]; then | ||
# The llvm-profdata merge command warns that the profraws were created from different binaries (which is true) but | ||
# works fine for what we care about (the s2n library). Therefore, for user clarity all output is suppressed. | ||
llvm-profdata merge -sparse tests/fuzz/profiles/*/*.profdata -o tests/fuzz/profiles/merged_fuzz.profdata > /dev/null 2>&1 | ||
|
||
printf "Calculating total s2n coverage... " | ||
llvm-cov report -instr-profile=tests/fuzz/profiles/merged_fuzz.profdata build/lib/libs2n.so ${FUZZCOV_SOURCES} > s2n_fuzz_coverage.txt | ||
|
||
# The llvm-profdata merge command warns that the profraws were created from different binaries (which is true) but | ||
# works fine for what we care about (the s2n library). Therefore, for user clarity all output is suppressed. | ||
llvm-profdata merge -sparse ./profiles/*/*.profdata -o ./profiles/s2n_cov.profdata > /dev/null 2>&1 | ||
llvm-cov report -instr-profile=./profiles/s2n_cov.profdata ${S2N_ROOT}/lib/libs2n.so ${FUZZCOV_SOURCES} > ${COVERAGE_DIR}/fuzz/s2n_cov.txt | ||
# convert coverage information to html format | ||
llvm-cov export -instr-profile=tests/fuzz/profiles/merged_fuzz.profdata build/lib/libs2n.so ${FUZZCOV_SOURCES} -format=lcov > s2n_fuzz_cov.info | ||
|
||
# Use LCOV format instead of HTML if the LLVM version we're using supports it | ||
if [[ $(grep -Eo "[0-9]*" <<< `llvm-cov --version` | head -1) > 8 ]]; then | ||
llvm-cov export -instr-profile=./profiles/s2n_cov.profdata ${S2N_ROOT}/lib/libs2n.so ${FUZZCOV_SOURCES} -format=lcov > ${COVERAGE_DIR}/fuzz/s2n_cov.info | ||
genhtml -q -o ${COVERAGE_DIR}/html/overall_fuzz_coverage ${COVERAGE_DIR}/fuzz/s2n_cov.info | ||
else | ||
llvm-cov show -instr-profile=./profiles/s2n_cov.profdata ${S2N_ROOT}/lib/libs2n.so ${FUZZCOV_SOURCES} -use-color -format=html > ${COVERAGE_DIR}/fuzz/s2n_cov.html | ||
fi | ||
# Generate coverage report compatible with codecov.io | ||
llvm-cov show -instr-profile=./profiles/s2n_cov.profdata ${S2N_ROOT}/lib/libs2n.so ${FUZZCOV_SOURCES} > ${COVERAGE_DIR}/fuzz/codecov.txt | ||
genhtml s2n_fuzz_cov.info --branch-coverage -q -o fuzz_coverage_report | ||
|
||
S2N_COV=`grep -Eo '[0-9]*\.[0-9]*\%' ${COVERAGE_DIR}/fuzz/s2n_cov.txt | tail -1` | ||
printf "total s2n coverage from fuzz tests: %s\n" $S2N_COV | ||
fi | ||
S2N_COV=`grep -Eo '[0-9]*\.[0-9]*\%' s2n_fuzz_coverage.txt | tail -3` | ||
LINE_COV=$(echo $S2N_COV | cut -d' ' -f1) | ||
FUNC_COV=$(echo $S2N_COV | cut -d' ' -f2) | ||
BRANCH_COV=$(echo $S2N_COV | cut -d' ' -f3) | ||
printf "Coverage Report:\nLine coverage: %s\nFunction coverage: %s\nBranch coverage: %s\n" "$LINE_COV" "$FUNC_COV" "$BRANCH_COV" | ||
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. These numbers are fuzz coverage for the entire repo(I assume). However, we don't fuzz test the entire repo. I would look for a more meaningful metric, like coverage of a single fuzz tested function. Otherwise it's probably better to leave this out? 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, it is the coverage stats for the entire repo. Hmm yeah the numbers themselves are probably not very valuable without being able to see the details. I can remove this from the output. We will have access to these numbers from the report anyways. For getting coverage of a single fuzz tested function, I'm not too sure how it will look like, but my guess is that each fuzz test has its own targeted functions (for example, I'm afraid adding that might complicate this PR, so I will have a follow-up PR after this one. 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. Created an issue for this: #4966 |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -18,11 +18,11 @@ | |||||
set -e | ||||||
|
||||||
usage() { | ||||||
echo "Usage: runFuzzTest.sh TEST_NAME FUZZ_TIMEOUT_SEC" | ||||||
echo "Usage: runFuzzTest.sh TEST_NAME FUZZ_TIMEOUT_SEC BUILD_DIR_PATH CORPUS_UPLOAD_LOC ARTIFACT_UPLOAD_LOC FUZZ_COVERAGE" | ||||||
exit 1 | ||||||
} | ||||||
|
||||||
if [ "$#" -ne "5" ]; then | ||||||
if [ "$#" -ne "7" ]; then | ||||||
usage | ||||||
fi | ||||||
|
||||||
|
@@ -31,6 +31,9 @@ FUZZ_TIMEOUT_SEC=$2 | |||||
BUILD_DIR_PATH=$3 | ||||||
CORPUS_UPLOAD_LOC=$4 | ||||||
ARTIFACT_UPLOAD_LOC=$5 | ||||||
FUZZ_COVERAGE=$6 | ||||||
S2N_ROOT=$7 | ||||||
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. Are you using this variable anywhere? It's sort of a historical holddover from the Makefiles... and between CodeBuild and CMake, there should be existing equivalents defined. 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. Ya, 7 CLI arguments is a lot, and I worry about the maintainability of this. How can we make things simpler? 1 - Remove Unnecessary OptionsMy understanding is that right now 2 - Separation of ConcernsGenerally it's best to "separate concerns". Don't unnecessarily couple things that don't need to be coupled. This applies to shell scripts that same way that it applies to actual code
I'd imagine an ideal "coverage pipeline" to look something like this
It seems odd that all of the coverage reporting is living inside a script called 3 - Scrutinize Coverage AssertionsI like the idea of asserting on some sort of coverage information. "feature coverage" from the fuzzer seems very nice and easy to assert on. Asserting on coverage from the actual report seems to requires a big blob of What benefit does this complicated stuff provide that feature coverage doesn't? And are our thresholds/assertions well-researched enough to actually be useful? I know we found that session ticket coverage issue when manually reviewing the code, so I question the value of these more complex assertions. 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. I believe I should be able to remove But 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. I did some refactoring so it only takes 3 CLI arguments instead of 7. Since runFuzzTest.sh: responsible for running the actual fuzz test On top of that, I removed couple of print statements that were hidden by CTest. I left the outputs that are useful for debugging in case fuzzing fails. |
||||||
|
||||||
MIN_TEST_PER_SEC="1000" | ||||||
MIN_FEATURES_COVERED="100" | ||||||
|
||||||
|
@@ -46,7 +49,7 @@ ASAN_OPTIONS+="symbolize=1" | |||||
LSAN_OPTIONS+="log_threads=1" | ||||||
UBSAN_OPTIONS+="print_stacktrace=1" | ||||||
jmayclin marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
NUM_CPU_THREADS=$(nproc) | ||||||
LIBFUZZER_ARGS+="-timeout=5 -max_len=4096 -print_final_stats=1 -jobs=${NUM_CPU_THREADS} -workers=${NUM_CPU_THREADS} -max_total_time=${FUZZ_TIMEOUT_SEC}" | ||||||
LIBFUZZER_ARGS+="-timeout=5 -max_len=4096 -print_final_stats=1 -workers=${NUM_CPU_THREADS} -max_total_time=${FUZZ_TIMEOUT_SEC}" | ||||||
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.
Suggested change
The 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. Removed this in the latest commit |
||||||
|
||||||
TEST_SPECIFIC_OVERRIDES="${BUILD_DIR_PATH}/lib/lib${TEST_NAME}_overrides.so" | ||||||
GLOBAL_OVERRIDES="${BUILD_DIR_PATH}/lib/libglobal_overrides.so" | ||||||
|
@@ -106,6 +109,7 @@ if [[ "$FUZZ_COVERAGE" == "true" ]]; then | |||||
mkdir -p "./profiles/${TEST_NAME}" | ||||||
rm -f ./profiles/${TEST_NAME}/*.profraw | ||||||
LLVM_PROFILE_FILE="./profiles/${TEST_NAME}/${TEST_NAME}.%p.profraw" \ | ||||||
env LD_PRELOAD="$LD_PRELOAD_" \ | ||||||
${BUILD_DIR_PATH}/bin/${TEST_NAME} ${LIBFUZZER_ARGS} ${TEMP_CORPUS_DIR} \ | ||||||
> ${TEST_NAME}_output.txt 2>&1 || ACTUAL_TEST_FAILURE=1 | ||||||
else | ||||||
|
@@ -129,16 +133,16 @@ declare -i TARGET_COV=0 | |||||
# If using LLVM version 9 or greater, coverage is output in LCOV format instead of HTML | ||||||
# All files are stored in the s2n coverage directory | ||||||
if [[ "$FUZZ_COVERAGE" == "true" ]]; then | ||||||
mkdir -p ${COVERAGE_DIR}/fuzz | ||||||
mkdir -p coverage/fuzz | ||||||
llvm-profdata merge -sparse ./profiles/${TEST_NAME}/*.profraw -o ./profiles/${TEST_NAME}/${TEST_NAME}.profdata | ||||||
llvm-cov report -instr-profile=./profiles/${TEST_NAME}/${TEST_NAME}.profdata ${S2N_ROOT}/lib/libs2n.so ${FUZZCOV_SOURCES} -show-functions > ${COVERAGE_DIR}/fuzz/${TEST_NAME}_cov.txt | ||||||
llvm-cov report -instr-profile=./profiles/${TEST_NAME}/${TEST_NAME}.profdata ${BUILD_DIR_PATH}/lib/libs2n.so ${FUZZCOV_SOURCES} -show-functions > coverage/fuzz/${TEST_NAME}_cov.txt | ||||||
|
||||||
# Use LCOV format instead of HTML if the LLVM version we're using supports it | ||||||
if [[ $(grep -Eo "[0-9]*" <<< `llvm-cov --version` | head -1) -gt 8 ]]; then | ||||||
llvm-cov export -instr-profile=./profiles/${TEST_NAME}/${TEST_NAME}.profdata ${S2N_ROOT}/lib/libs2n.so ${FUZZCOV_SOURCES} -format=lcov > ${COVERAGE_DIR}/fuzz/${TEST_NAME}_cov.info | ||||||
genhtml -q -o ${COVERAGE_DIR}/html/${TEST_NAME} ${COVERAGE_DIR}/fuzz/${TEST_NAME}_cov.info | ||||||
llvm-cov export -instr-profile=./profiles/${TEST_NAME}/${TEST_NAME}.profdata ${BUILD_DIR_PATH}/lib/libs2n.so ${FUZZCOV_SOURCES} -format=lcov > coverage/fuzz/${TEST_NAME}_cov.info | ||||||
genhtml -q -o coverage/html/${TEST_NAME} coverage/fuzz/${TEST_NAME}_cov.info | ||||||
else | ||||||
llvm-cov show -instr-profile=./profiles/${TEST_NAME}/${TEST_NAME}.profdata ${S2N_ROOT}/lib/libs2n.so ${FUZZCOV_SOURCES} -use-color -format=html > ${COVERAGE_DIR}/fuzz/${TEST_NAME}_cov.html | ||||||
llvm-cov show -instr-profile=./profiles/${TEST_NAME}/${TEST_NAME}.profdata ${BUILD_DIR_PATH}/lib/libs2n.so ${FUZZCOV_SOURCES} -use-color -format=html > coverage/fuzz/${TEST_NAME}_cov.html | ||||||
fi | ||||||
|
||||||
# Extract target functions from test source | ||||||
|
@@ -149,8 +153,8 @@ if [[ "$FUZZ_COVERAGE" == "true" ]]; then | |||||
then | ||||||
for TARGET in ${TARGET_FUNCS} | ||||||
do | ||||||
TARGET_TOTAL+=`sed -n "s/^.*${TARGET} .*% *\([0-9]*\) .*$/\1/p" ${COVERAGE_DIR}/fuzz/${TEST_NAME}_cov.txt` | ||||||
TARGET_COV+=`sed -n "s/^.*${TARGET} .*% *[0-9]* *\([0-9]*\) .*$/\1/p" ${COVERAGE_DIR}/fuzz/${TEST_NAME}_cov.txt` | ||||||
TARGET_TOTAL+=`sed -n "s/^.*${TARGET} .*% *\([0-9]*\) .*$/\1/p" coverage/fuzz/${TEST_NAME}_cov.txt` | ||||||
TARGET_COV+=`sed -n "s/^.*${TARGET} .*% *[0-9]* *\([0-9]*\) .*$/\1/p" coverage/fuzz/${TEST_NAME}_cov.txt` | ||||||
done | ||||||
fi | ||||||
fi | ||||||
|
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.
Is there a reason that we can't just use
COVERAGE
?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.
You're right, I can just use
COVERAGE
😓. Thank you for pointing it out