diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 61bc8e501ec9..fa6d1f80ae0c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -136,6 +136,7 @@ jobs: shell: bash run: | set -ex + set -o pipefail git config --global --add safe.directory /__w/CCF/CCF # Ensure all files have timestamps strictly after SOURCE_DATE_EPOCH, # otherwise rpmbuild will not clamp to SOURCE_DATE_EPOCH and the build @@ -143,7 +144,7 @@ jobs: find . -type f -exec touch {} + mkdir build cd build - cmake -GNinja -DCLIENT_PROTOCOLS_TEST=ON -DCMAKE_BUILD_TYPE=Release .. + cmake -GNinja -DCLIENT_PROTOCOLS_TEST=ON -DCCF_ENABLE_RELEASE_HARDENING=ON -DCMAKE_BUILD_TYPE=Release .. ninja -v | tee build.log - name: "Test" @@ -182,7 +183,7 @@ jobs: # Reset cmake config to affect cpack settings rm CMakeCache.txt - cmake -GNinja -DCMAKE_BUILD_TYPE=Release .. + cmake -GNinja -DCCF_ENABLE_RELEASE_HARDENING=ON -DCMAKE_BUILD_TYPE=Release .. cmake -L .. 2>/dev/null | grep CMAKE_INSTALL_PREFIX: | cut -d = -f 2 > /tmp/install_prefix cpack -V -G RPM diff --git a/CMakeLists.txt b/CMakeLists.txt index 67e47b6eaebd..0a1de9758ff7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,6 +19,23 @@ message(STATUS "CCF version = ${CCF_VERSION}") message(STATUS "CCF release version = ${CCF_RELEASE_VERSION}") message(STATUS "CCF version suffix = ${CCF_VERSION_SUFFIX}") +option( + CCF_ENABLE_RELEASE_HARDENING + "Enable supported compiler and linker hardening options for release configurations" + ON +) + +# Enable common release-build hardening where the active toolchain supports it: +# -fstack-protector-strong adds canary checks to functions with vulnerable stack +# objects, -D_FORTIFY_SOURCE=2 enables fortified libc wrappers when optimisation +# is enabled, -fstack-clash-protection probes large stack allocations, and +# -z relro/-z now make dynamic relocation metadata read-only after eager binding. +# See GCC's instrumentation options +# (https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html), glibc's +# fortification documentation +# (https://sourceware.org/glibc/wiki/Source_Fortification), and GNU ld's options +# (https://sourceware.org/binutils/docs/ld/Options.html). + # Set the default install prefix for CCF. Users may override this value with the # cmake command. For example: # @@ -215,6 +232,7 @@ target_include_directories(ccf_launcher PRIVATE ${CCF_GENERATED_DIR}) # HTTP parser add_library(http_parser "${HTTP_PARSER_SOURCES}") set_property(TARGET http_parser PROPERTY POSITION_INDEPENDENT_CODE ON) +add_hardening(http_parser) install(TARGETS http_parser EXPORT ccf DESTINATION lib) # CCF platform agnostic library @@ -499,6 +517,7 @@ target_link_libraries( verify_uvm_attestation_and_endorsements PRIVATE ccf_pal ccf ) +add_hardening(verify_uvm_attestation_and_endorsements) install(TARGETS verify_uvm_attestation_and_endorsements DESTINATION bin) # SNP attestation fetching and verification binary @@ -510,6 +529,7 @@ target_link_libraries( verify_attestation PRIVATE ccf_pal ccf_tasks uv curl http_parser ) +add_hardening(verify_attestation) install(TARGETS verify_attestation DESTINATION bin) if(BUILD_TESTS) @@ -1458,4 +1478,5 @@ install( # Perf tool executable (requires Arrow/Parquet, not compatible with GLIBCXX_DEBUG) if(NOT GLIBCXX_DEBUG) include(${CCF_DIR}/tests/perf-system/submitter/CMakeLists.txt) + add_hardening(submit) endif() diff --git a/cmake/ccf_app.cmake b/cmake/ccf_app.cmake index a6e324ea9718..d104052bac24 100644 --- a/cmake/ccf_app.cmake +++ b/cmake/ccf_app.cmake @@ -41,6 +41,7 @@ function(add_ccf_app name) set_property(TARGET ${name} PROPERTY POSITION_INDEPENDENT_CODE ON) add_san(${name}) + add_hardening(${name}) add_tidy(${name}) enable_coverage(${name}) @@ -68,6 +69,7 @@ function(add_ccf_static_library name) set_property(TARGET ${name} PROPERTY POSITION_INDEPENDENT_CODE ON) add_san(${name}) + add_hardening(${name}) add_tidy(${name}) add_warning_checks(${name}) diff --git a/cmake/crypto.cmake b/cmake/crypto.cmake index a17450e8d1b4..57d9c958c337 100644 --- a/cmake/crypto.cmake +++ b/cmake/crypto.cmake @@ -35,6 +35,7 @@ find_library(TLS_LIBRARY ssl) add_library(ccfcrypto STATIC ${CCFCRYPTO_SRC}) add_san(ccfcrypto) +add_hardening(ccfcrypto) add_tidy(ccfcrypto) target_link_libraries(ccfcrypto PUBLIC crypto ssl evercbor) diff --git a/cmake/evercbor.cmake b/cmake/evercbor.cmake index 7d387dbf68d7..a34e9e538dd4 100644 --- a/cmake/evercbor.cmake +++ b/cmake/evercbor.cmake @@ -15,5 +15,6 @@ target_include_directories( target_compile_options(evercbor PRIVATE -Wno-everything) set_property(TARGET evercbor PROPERTY POSITION_INDEPENDENT_CODE ON) add_san(evercbor) +add_hardening(evercbor) install(TARGETS evercbor EXPORT ccf DESTINATION lib) diff --git a/cmake/gersemi_definitions.cmake b/cmake/gersemi_definitions.cmake index 68143b27293d..d11c2795ed81 100644 --- a/cmake/gersemi_definitions.cmake +++ b/cmake/gersemi_definitions.cmake @@ -67,6 +67,9 @@ endfunction() function(add_san name) endfunction() +function(add_hardening name) +endfunction() + function(add_tidy name) endfunction() diff --git a/cmake/quickjs.cmake b/cmake/quickjs.cmake index 6df4483a8f65..c4ce7d1345ef 100644 --- a/cmake/quickjs.cmake +++ b/cmake/quickjs.cmake @@ -31,6 +31,7 @@ target_compile_options( PRIVATE $<$:-DDUMP_LEAKS> ) add_san(quickjs) +add_hardening(quickjs) set_property(TARGET quickjs PROPERTY POSITION_INDEPENDENT_CODE ON) target_include_directories( quickjs diff --git a/cmake/tools.cmake b/cmake/tools.cmake index 7c5006041045..0585dc33a64f 100644 --- a/cmake/tools.cmake +++ b/cmake/tools.cmake @@ -43,6 +43,68 @@ function(add_san name) endif() endfunction() +function(add_hardening name) + if(NOT CCF_ENABLE_RELEASE_HARDENING) + return() + endif() + + include(CheckCompilerFlag) + include(CheckLinkerFlag) + + set( + release_configuration + "$,$,$>" + ) + + foreach(lang C CXX) + foreach( + flag + IN + ITEMS + "-fstack-protector-strong" + "-D_FORTIFY_SOURCE=2" + "-fstack-clash-protection" + ) + string(MAKE_C_IDENTIFIER "${lang}_${flag}" flag_id) + set(supported_var "CCF_${flag_id}_SUPPORTED") + check_compiler_flag(${lang} "${flag}" ${supported_var}) + if(${supported_var}) + target_compile_options( + ${name} + PRIVATE + "$<$>:${flag}>" + ) + endif() + endforeach() + endforeach() + + # Static, object, and interface libraries do not perform a link step here, but + # compile hardening still applies to their object files. + get_target_property(target_type ${name} TYPE) + if( + target_type STREQUAL "STATIC_LIBRARY" + OR target_type STREQUAL "OBJECT_LIBRARY" + OR target_type STREQUAL "INTERFACE_LIBRARY" + ) + return() + endif() + + foreach(lang C CXX) + foreach(flag IN ITEMS "LINKER:-z,relro" "LINKER:-z,now") + string(MAKE_C_IDENTIFIER "${lang}_${flag}" flag_id) + set(supported_var "CCF_${flag_id}_SUPPORTED") + check_linker_flag(${lang} "${flag}" ${supported_var}) + if(${supported_var}) + target_link_options( + ${name} + PRIVATE + "$<$>:${flag}>" + ) + endif() + endforeach() + endforeach() +endfunction() + function(add_tidy name) set_target_properties( ${name} diff --git a/reproduce/reproduce_rpm.sh b/reproduce/reproduce_rpm.sh index d18499cda19c..c4232a412337 100755 --- a/reproduce/reproduce_rpm.sh +++ b/reproduce/reproduce_rpm.sh @@ -5,7 +5,7 @@ # This script is intended to be called from start_container_and_reproduce_rpm.sh script. # builds the RPM using timestamps from the JSON and outputs the package. -set -exu +set -exuo pipefail usage() { echo "Usage: $0 " @@ -26,10 +26,10 @@ build_pkg() { mkdir -p /tmp/reproduced mkdir -p build && cd build echo "Reproducing CCF package..." - cmake -G Ninja -DCLIENT_PROTOCOLS_TEST=ON -DCMAKE_BUILD_TYPE=Release .. + cmake -G Ninja -DCLIENT_PROTOCOLS_TEST=ON -DCCF_ENABLE_RELEASE_HARDENING=ON -DCMAKE_BUILD_TYPE=Release .. ninja -v rm CMakeCache.txt - cmake -G Ninja -DCMAKE_BUILD_TYPE=Release .. + cmake -G Ninja -DCCF_ENABLE_RELEASE_HARDENING=ON -DCMAKE_BUILD_TYPE=Release .. cmake -L .. 2>/dev/null | grep CMAKE_INSTALL_PREFIX: | cut -d = -f 2 > /tmp/install_prefix cpack -V -G RPM D_INITIAL_PKG=`ls *.rpm` @@ -46,4 +46,3 @@ REPRO_JSON="$1" setup_env install_deps build_pkg -