diff --git a/CMakeLists.txt b/CMakeLists.txt index 5ad6aea68d6..66c2580ae90 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -145,7 +145,7 @@ elseif (UNSAFE_TREAT_WARNINGS_AS_ERRORS) target_compile_options(${PROJECT_NAME} PRIVATE -Werror ) endif () -if(BUILD_TESTING AND BUILD_SHARED_LIBS) +if(BUILD_TESTING AND BUILD_SHARED_LIBS OR S2N_FUZZ_TEST) target_compile_options(${PROJECT_NAME} PRIVATE -fvisibility=default) else() target_compile_options(${PROJECT_NAME} PRIVATE -fvisibility=hidden -DS2N_EXPORTS) @@ -183,8 +183,9 @@ if(NO_STACK_PROTECTOR) target_compile_options(${PROJECT_NAME} PRIVATE -Wstack-protector -fstack-protector-all) endif() -if(S2N_UNSAFE_FUZZING_MODE) - target_compile_options(${PROJECT_NAME} PRIVATE -fsanitize-coverage=trace-pc-guard -fsanitize=address,undefined,leak -fuse-ld=gold -DS2N_ADDRESS_SANITIZER=1) +if(S2N_FUZZ_TEST) + target_compile_options(${PROJECT_NAME} PUBLIC -fsanitize-coverage=trace-pc-guard -fsanitize=leak) + target_link_libraries(${PROJECT_NAME} PUBLIC -fsanitize-coverage=trace-pc-guard -fsanitize=leak) endif() if(TSAN) @@ -587,6 +588,92 @@ if (BUILD_TESTING) set_property(TEST ${test_target} PROPERTY TIMEOUT 7200) endforeach() endif() + + if(S2N_FUZZ_TEST) + message(STATUS "Fuzz build enabled") + set(SCRIPT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/tests/fuzz/runFuzzTest.sh") + file(GLOB FUZZ_TEST_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/tests/fuzz/*.c") + + file(GLOB TESTLIB_SRC "tests/testlib/*.c") + file(GLOB TESTLIB_HEADERS "tests/testlib/*.h" "tests/s2n_test.h") + + add_library(fuzztest SHARED ${TESTLIB_HEADERS} ${TESTLIB_SRC}) + target_include_directories(fuzztest PUBLIC tests) + target_link_libraries(fuzztest PUBLIC ${PROJECT_NAME}) + + # Set default values for fuzzing if not defined + if(NOT DEFINED LIBFUZZER_LIB) + message(FATAL_ERROR "LIBFUZZER_LIB is not defined. Please set it to the path of your libFuzzer.a.") + endif() + + if(NOT DEFINED FUZZ_TIMEOUT_SEC) + set(FUZZ_TIMEOUT_SEC 60) + endif() + + if(NOT DEFINED CORPUS_UPLOAD_LOC) + set(CORPUS_UPLOAD_LOC "none") + endif() + + if(NOT DEFINED ARTIFACT_UPLOAD_LOC) + set(ARTIFACT_UPLOAD_LOC "none") + endif() + + if(NOT DEFINED FUZZ_TESTS) + set(FUZZ_TESTS "${TESTS}") + endif() + + # Build LD_PRELOAD shared libraries + set(LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests/fuzz/LD_PRELOAD) + file(GLOB LIBRARY_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/tests/fuzz/LD_PRELOAD/*.c") + foreach(SRC ${LIBRARY_SRCS}) + get_filename_component(LIB_NAME ${SRC} NAME_WE) + add_library(${LIB_NAME} SHARED ${SRC}) + target_include_directories(${LIB_NAME} PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/api + ) + # Set the output directory and remove the default "lib" prefix + set_target_properties(${LIB_NAME} PROPERTIES + PREFIX "" + LIBRARY_OUTPUT_DIRECTORY ${LIBRARY_OUTPUT_DIRECTORY} + ) + target_compile_options(${LIB_NAME} PRIVATE + -fPIC + ) + endforeach() + + set(CMAKE_C_COMPILER clang) + + foreach(src ${FUZZ_TEST_SRCS}) + get_filename_component(TEST_NAME ${src} NAME_WE) + + add_executable(${TEST_NAME} ${src}) + target_include_directories(${TEST_NAME} PRIVATE ./) + + target_compile_options(${TEST_NAME} PRIVATE + -g -Wno-unknown-pragmas -Wno-unused-result + ) + target_link_libraries(${TEST_NAME} PRIVATE + fuzztest + ${LIBFUZZER_LIB} # Manually link old libFuzzer.a + -lstdc++ + ) + + # Set the output directory for the fuzzing binaries + set(FUZZ_BIN_DIR "${CMAKE_CURRENT_SOURCE_DIR}/tests/fuzz") + set_target_properties(${TEST_NAME} PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${FUZZ_BIN_DIR} + ) + + add_test(NAME ${TEST_NAME} + COMMAND ${CMAKE_COMMAND} -E env + DYLD_LIBRARY_PATH=${CMAKE_CURRENT_BINARY_DIR}/lib:${CMAKE_CURRENT_BINARY_DIR}/tests/testlib:${CMAKE_CURRENT_SOURCE_DIR}/libcrypto-root/lib:$ENV{DYLD_LIBRARY_PATH} + LD_LIBRARY_PATH=${CMAKE_CURRENT_BINARY_DIR}/lib:${CMAKE_CURRENT_BINARY_DIR}/tests/testlib:${CMAKE_CURRENT_SOURCE_DIR}/libcrypto-root/lib:$ENV{LD_LIBRARY_PATH} + bash ${SCRIPT_PATH} ${TEST_NAME} ${FUZZ_TIMEOUT_SEC} ${CORPUS_UPLOAD_LOC} ${ARTIFACT_UPLOAD_LOC} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests/fuzz) + set_property(TEST ${TEST_NAME} PROPERTY LABELS "fuzz") + endforeach() + endif() endif() #install the s2n files diff --git a/codebuild/bin/s2n_codebuild.sh b/codebuild/bin/s2n_codebuild.sh index 68e8b4c4e6e..64a53a57b12 100755 --- a/codebuild/bin/s2n_codebuild.sh +++ b/codebuild/bin/s2n_codebuild.sh @@ -127,6 +127,7 @@ if [[ "$TESTS" == "ALL" || "$TESTS" == "integrationv2" ]]; then run_integration_ if [[ "$TESTS" == "ALL" || "$TESTS" == "crt" ]]; then ./codebuild/bin/build_aws_crt_cpp.sh $(mktemp -d) $(mktemp -d); fi if [[ "$TESTS" == "ALL" || "$TESTS" == "sharedandstatic" ]]; then ./codebuild/bin/test_install_shared_and_static.sh $(mktemp -d); fi if [[ "$TESTS" == "ALL" || "$TESTS" == "dynamicload" ]]; then ./codebuild/bin/test_dynamic_load.sh $(mktemp -d); fi +# This can be removed after CI starts using buildspec_fuzz.yml if [[ "$TESTS" == "ALL" || "$TESTS" == "fuzz" ]]; then (make clean && make fuzz) ; fi if [[ "$TESTS" == "sawHMAC" ]] && [[ "$OS_NAME" == "linux" ]]; then make -C tests/saw/ tmp/verify_HMAC.log ; fi if [[ "$TESTS" == "sawDRBG" ]]; then make -C tests/saw tmp/verify_drbg.log ; fi diff --git a/codebuild/spec/buildspec_fuzz.yml b/codebuild/spec/buildspec_fuzz.yml new file mode 100644 index 00000000000..40031a6b569 --- /dev/null +++ b/codebuild/spec/buildspec_fuzz.yml @@ -0,0 +1,71 @@ +--- +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You may not use +# this file except in compliance with the License. A copy of the License is +# located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing permissions and +# limitations under the License. +version: 0.2 + +# This buildspec runs on an Ubuntu22 image. That configuration is a property of +# the codebuild job itself. + +# Codebuild's matrix jobs have non-differentiated names so use batch-list +# instead. + +# Parameter motivation + +# LIBCRYPTOS +# awslc: happy path libcrypto for s2n-tls +# openssl 3: s2n-tls takes different code paths for ossl3, so make sure we run +# asan on it. See pr 4033 for a historical motivating example. + +batch: + build-list: + - identifier: clang_awslc + debug-session: true + env: + compute-type: BUILD_GENERAL1_XLARGE + image: 024603541914.dkr.ecr.us-west-2.amazonaws.com/docker:ubuntu22codebuild + privileged-mode: true + variables: + S2N_LIBCRYPTO: awslc + COMPILER: clang + - identifier: clang_openssl_3_0 + debug-session: true + env: + compute-type: BUILD_GENERAL1_XLARGE + image: 024603541914.dkr.ecr.us-west-2.amazonaws.com/docker:ubuntu22codebuild + privileged-mode: true + variables: + S2N_LIBCRYPTO: openssl-3.0 + COMPILER: clang + +phases: + pre_build: + commands: + - | + if [ -d "third-party-src" ]; then + cd third-party-src; + fi + - /usr/bin/$COMPILER --version + build: + on-failure: ABORT + commands: + - | + cmake . -Bbuild \ + -DCMAKE_PREFIX_PATH=$LIBCRYPTO_ROOT \ + -DS2N_FUZZ_TEST=on \ + -DLIBFUZZER_LIB=/usr/local/libfuzzer/lib/libFuzzer.a + - cmake --build ./build -- -j $(nproc) + post_build: + on-failure: ABORT + commands: + # -L: Restrict tests to names matching the pattern 'fuzz' + - cmake --build build/ --target test -- ARGS="-L fuzz --output-on-failure" diff --git a/tests/fuzz/runFuzzTest.sh b/tests/fuzz/runFuzzTest.sh index 79a275defc5..58517ea302f 100755 --- a/tests/fuzz/runFuzzTest.sh +++ b/tests/fuzz/runFuzzTest.sh @@ -52,11 +52,12 @@ GLOBAL_OVERRIDES="${PWD}/LD_PRELOAD/global_overrides.so" FUZZCOV_SOURCES="${S2N_ROOT}/api ${S2N_ROOT}/bin ${S2N_ROOT}/crypto ${S2N_ROOT}/error ${S2N_ROOT}/stuffer ${S2N_ROOT}/tls ${S2N_ROOT}/utils" +# Use LD_PRELOAD_ to prevent symbol lookup errors in commands like mkdir. if [ -e $TEST_SPECIFIC_OVERRIDES ]; then - export LD_PRELOAD="$TEST_SPECIFIC_OVERRIDES $GLOBAL_OVERRIDES" + export LD_PRELOAD_="$TEST_SPECIFIC_OVERRIDES $GLOBAL_OVERRIDES" else - export LD_PRELOAD="$GLOBAL_OVERRIDES" + export LD_PRELOAD_="$GLOBAL_OVERRIDES" fi FIPS_TEST_MSG="" @@ -143,7 +144,7 @@ if [[ "$FUZZ_COVERAGE" == "true" ]]; then rm -f ./profiles/${TEST_NAME}/*.profraw LLVM_PROFILE_FILE="./profiles/${TEST_NAME}/${TEST_NAME}.%p.profraw" ./${TEST_NAME} ${LIBFUZZER_ARGS} ${TEMP_CORPUS_DIR} > ${TEST_NAME}_output.txt 2>&1 || ACTUAL_TEST_FAILURE=1 else - ./${TEST_NAME} ${LIBFUZZER_ARGS} ${TEMP_CORPUS_DIR} > ${TEST_NAME}_output.txt 2>&1 || ACTUAL_TEST_FAILURE=1 + env LD_PRELOAD="$LD_PRELOAD_" ./${TEST_NAME} ${LIBFUZZER_ARGS} ${TEMP_CORPUS_DIR} > ${TEST_NAME}_output.txt 2>&1 || ACTUAL_TEST_FAILURE=1 fi TEST_INFO=$(