Add hardening build options

This commit enables hardened-specific checks and codegen, inspired by
GCC 14's -fhardened command line switch and LLVM/libc++'s hardened
modes.

We enable (depending on compiler capabilities):

* -ftrivial-auto-var-init=pattern;
* -fstack-protector-strong;
* -fstack-clash-protection;
* -fcf-protection=full or /CETCOMPAT;
* -D_FORTIFY_SOURCE=3 or 2 on Glibc, depending on the Glibc version,
  provided that some optimization level is enabled (release build or
  optimized debug build);
* on libstdc++, -D_GLIBCXX_ASSERTIONS;
* on libc++, -D_LIBCPP_HARDENING_MODE set to
  _LIBCPP_HARDENING_MODE_EXTENSIVE in debug and to
  _LIBCPP_HARDENING_MODE_FAST in release (_DEBUG is too slow);
* -Wl,-z,relro,-z,now.

This aligns us 100% with -fhardened (we already pass -fPIE and -pie
anyhow). Some Linux distributions already ship GCC/Clang with some of
these options enabled by default.

The check for Intel CET has been amended to always test if the compiler
supports the corresponding flag; and, if so, enable the feature. Before,
it was behind a configure option and the test only checked if the
compiler had CET support automatically active (the test didn't pass
-fcf-protection to the compiler).

The check for -fstack-protector-strong has been made general (rather
than QNX-specific). We don't support QNX < 7 anyhow.

Finally, the qt_config_linker_supports_flag_test test has been
amended to also support MSVC.

All of the hardening options are enabled by default.

[ChangeLog][Build System] Qt builds by default in "hardened mode",
meaning that a series of security-related compiler options are
automatically enabled. In the unlikely case in which these options
constitute an unacceptable performance hit, it is possible to disable
individual hardening options when configuring Qt.

Change-Id: I2c026b0438010ad10d5e7b1136fedf4ae3af8822
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Giuseppe D'Angelo 2024-03-06 16:22:12 +01:00
parent a89a916377
commit 9ff1e6d80b
5 changed files with 203 additions and 35 deletions

View File

@ -1359,7 +1359,16 @@ function(qt_config_linker_supports_flag_test name)
endif() endif()
cmake_parse_arguments(arg "" "LABEL;FLAG" "" ${ARGN}) cmake_parse_arguments(arg "" "LABEL;FLAG" "" ${ARGN})
set(flags "-Wl,${arg_FLAG}") if(GCC OR CLANG)
set(flags "-Wl,--fatal-warnings,${arg_FLAG}")
elseif(MSVC)
set(flags "${arg_FLAG}")
else()
# We don't know how to pass linker options in a way that
# it reliably fails, so assume the detection failed.
set(TEST_${name} "0" CACHE INTERNAL "${label}")
return()
endif()
# Pass the linker that the main project uses to the compile test. # Pass the linker that the main project uses to the compile test.
qt_internal_get_active_linker_flags(linker_flags) qt_internal_get_active_linker_flags(linker_flags)

View File

@ -316,18 +316,52 @@ if (GCC AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "9.2")
target_compile_options(PlatformCommonInternal INTERFACE $<$<COMPILE_LANGUAGE:CXX>:-Wsuggest-override>) target_compile_options(PlatformCommonInternal INTERFACE $<$<COMPILE_LANGUAGE:CXX>:-Wsuggest-override>)
endif() endif()
# Hardening options
if(QT_FEATURE_intelcet) if(QT_FEATURE_intelcet)
if(MSVC) if(MSVC)
qt_internal_platform_link_options(PlatformCommonInternal INTERFACE qt_internal_platform_link_options(PlatformCommonInternal INTERFACE -CETCOMPAT)
-CETCOMPAT
)
else() else()
target_compile_options(PlatformCommonInternal INTERFACE target_compile_options(PlatformCommonInternal INTERFACE -fcf-protection=full)
-fcf-protection=full
)
endif() endif()
endif() endif()
if(QT_FEATURE_glibc_fortify_source)
set(is_optimized_build "$<OR:$<NOT:$<CONFIG:Debug>>,$<BOOL:${QT_FEATURE_optimize_debug}>>")
# Some compilers may define _FORTIFY_SOURCE by default when optimizing, remove it
# before defining our own
target_compile_options(PlatformCommonInternal BEFORE INTERFACE "$<${is_optimized_build}:-U_FORTIFY_SOURCE>")
if(TEST_glibc_234)
target_compile_options(PlatformCommonInternal INTERFACE "$<${is_optimized_build}:-D_FORTIFY_SOURCE=3>")
else()
target_compile_options(PlatformCommonInternal INTERFACE "$<${is_optimized_build}:-D_FORTIFY_SOURCE=2>")
endif()
endif()
if(QT_FEATURE_trivial_auto_var_init_pattern)
target_compile_options(PlatformCommonInternal INTERFACE -ftrivial-auto-var-init=pattern)
endif()
if(QT_FEATURE_stack_protector)
target_compile_options(PlatformCommonInternal INTERFACE -fstack-protector-strong)
endif()
if(QT_FEATURE_stack_clash_protection)
target_compile_options(PlatformCommonInternal INTERFACE -fstack-clash-protection)
endif()
if(QT_FEATURE_libstdcpp_assertions)
target_compile_definitions(PlatformCommonInternal INTERFACE _GLIBCXX_ASSERTIONS)
endif()
if(QT_FEATURE_libcpp_hardening)
target_compile_definitions(PlatformCommonInternal INTERFACE -D_LIBCPP_HARDENING_MODE="$<IF:$<CONFIG:Debug>,_LIBCPP_HARDENING_MODE_EXTENSIVE,_LIBCPP_HARDENING_MODE_FAST>")
endif()
if(QT_FEATURE_relro_now_linker)
qt_internal_platform_link_options(PlatformCommonInternal INTERFACE "-Wl,-z,relro,-z,now")
endif()
if(QT_FEATURE_force_asserts) if(QT_FEATURE_force_asserts)
target_compile_definitions(PlatformCommonInternal INTERFACE QT_FORCE_ASSERTS) target_compile_definitions(PlatformCommonInternal INTERFACE QT_FORCE_ASSERTS)
endif() endif()

View File

@ -138,7 +138,16 @@ Build options:
-pch ................. Use precompiled headers [auto] -pch ................. Use precompiled headers [auto]
-ltcg ................ Use Link Time Code Generation [no] -ltcg ................ Use Link Time Code Generation [no]
-intelcet ............ Use Intel Control-flow Enforcement Technology [no] -intelcet ............ Use Intel Control-flow Enforcement Technology [auto]
-glibc-fortify-source Use Glibc function fortification [auto]
-trivial-auto-var-init-pattern
Use -ftrivial-auto-var-init=pattern [auto]
-stack-protector ..... Use -fstack-protector-strong [auto]
-stack-clash-protection
Use -fstack-clash-protection [auto]
-libstdcpp-assertions Use libstdc++ assertions [auto]
-libcpp-hardening .... Use libc++ hardening [auto]
-relro-now-linker .... Use -z relro -z now when linking [auto]
-linker [bfd,gold,lld,mold] -linker [bfd,gold,lld,mold]
Force use of the GNU ld, GNU gold, LLVM/LLD or mold Force use of the GNU ld, GNU gold, LLVM/LLD or mold
linker instead of default one (GCC and clang only) linker instead of default one (GCC and clang only)

View File

@ -211,6 +211,28 @@ endif()
# machineTuple # machineTuple
qt_config_compile_test_machine_tuple("machine tuple") qt_config_compile_test_machine_tuple("machine tuple")
# glibc
qt_config_compile_test(glibc
LABEL "Using Glibc"
CODE
"#include <features.h>
#ifndef __GLIBC__
#error
#endif
int main() {}"
)
# glibc 2.34, for _FORTIFY_SOURCE == 3
qt_config_compile_test(glibc_234
LABEL "Using Glibc >= 2.34"
CODE
"#include <features.h>
#if !defined(__GLIBC__) || !__GLIBC_PREREQ(2, 34)
#error
#endif
int main() {}"
)
# cxx20 # cxx20
qt_config_compile_test(cxx20 qt_config_compile_test(cxx20
LABEL "C++20 support" LABEL "C++20 support"
@ -398,29 +420,40 @@ alloca(1);
") ")
# stack_protector # stack_protector
qt_config_compile_test(stack_protector if(NOT WASM)
LABEL "stack protection" # emcc doesn't support this, but the detection accidentally succeeds
COMPILE_OPTIONS -fstack-protector-strong # https://github.com/emscripten-core/emscripten/issues/17030
CODE qt_config_compiler_supports_flag_test(stack_protector
"#ifdef __QNXNTO__ LABEL "stack protection"
# include <sys/neutrino.h> FLAG "-fstack-protector-strong"
# if _NTO_VERSION < 700 )
# error stack-protector not used (by default) before QNX 7.0.0. endif()
# endif
#endif
int main(void) # stack_clash_protection
{ if(NOT CLANG) # https://gitlab.kitware.com/cmake/cmake/-/issues/21998
/* BEGIN TEST: */ qt_config_compiler_supports_flag_test(stack_clash_protection
/* END TEST: */ LABEL "-fstack-clash-protection support"
return 0; FLAG "-fstack-clash-protection"
} )
") endif()
# trivial_auto_var_init_pattern
qt_config_compiler_supports_flag_test(trivial_auto_var_init_pattern
LABEL "-ftrivial-auto-var-init=pattern support"
FLAG "-ftrivial-auto-var-init=pattern"
)
# intelcet # intelcet
qt_config_compile_test(intelcet if(MSVC)
LABEL "Support for Intel Control-flow Enforcement Technology (CET)" qt_config_linker_supports_flag_test(intelcet
CODE LABEL "Support for Intel Control-flow Enforcement Technology (CET)"
FLAG "-CETCOMPAT"
)
else()
qt_config_compile_test(intelcet
LABEL "Support for Intel Control-flow Enforcement Technology (CET)"
COMPILE_OPTIONS -fcf-protection=full
CODE
"int main(void) "int main(void)
{ {
/* BEGIN TEST: */ /* BEGIN TEST: */
@ -430,8 +463,35 @@ qt_config_compile_test(intelcet
/* END TEST: */ /* END TEST: */
return 0; return 0;
} }
") "
)
endif()
# -z relro -z now
if(NOT WIN32)
qt_config_linker_supports_flag_test(relro_now_linker
LABEL "Support for -z relro and -z now"
FLAG "-z,relro,-z,now"
)
endif()
# Is libc++ the default Standard Library?
qt_config_compile_test(using_stdlib_libcpp
LABEL "Compiler defaults to libc++"
CODE
"
#include <ciso646>
int main(void)
{
/* BEGIN TEST: */
#ifndef _LIBCPP_VERSION
# error
#endif
/* END TEST: */
}
"
)
#### Features #### Features
@ -889,10 +949,6 @@ qt_feature("alloca" PRIVATE
LABEL "alloca()" LABEL "alloca()"
CONDITION QT_FEATURE_alloca_h OR QT_FEATURE_alloca_malloc_h OR TEST_alloca_stdlib_h CONDITION QT_FEATURE_alloca_h OR QT_FEATURE_alloca_malloc_h OR TEST_alloca_stdlib_h
) )
qt_feature("stack-protector-strong" PRIVATE
LABEL "stack protection"
CONDITION QNX AND TEST_stack_protector
)
qt_feature("system-zlib" PRIVATE qt_feature("system-zlib" PRIVATE
LABEL "Using system zlib" LABEL "Using system zlib"
CONDITION WrapSystemZLIB_FOUND CONDITION WrapSystemZLIB_FOUND
@ -1056,10 +1112,56 @@ qt_feature("relocatable" PRIVATE
AUTODETECT QT_FEATURE_shared AUTODETECT QT_FEATURE_shared
CONDITION QT_FEATURE_dlopen OR WIN32 OR NOT QT_FEATURE_shared CONDITION QT_FEATURE_dlopen OR WIN32 OR NOT QT_FEATURE_shared
) )
# hardening features
qt_feature("intelcet" PRIVATE qt_feature("intelcet" PRIVATE
LABEL "Using Intel CET" LABEL "Using Intel Control-flow Enforcement Technology (CET)"
CONDITION ( INPUT_intelcet STREQUAL yes ) OR TEST_intelcet AUTODETECT ON
CONDITION TEST_intelcet
) )
qt_feature_config("intelcet" QMAKE_PUBLIC_CONFIG)
qt_feature("glibc_fortify_source" PRIVATE
LABEL "Using Glibc function fortification"
AUTODETECT ON
CONDITION TEST_glibc
)
qt_feature_config("glibc_fortify_source" QMAKE_PUBLIC_CONFIG)
qt_feature("trivial_auto_var_init_pattern" PRIVATE
LABEL "Using -ftrivial-auto-var-init=pattern"
AUTODETECT ON
CONDITION TEST_trivial_auto_var_init_pattern
)
qt_feature_config("trivial_auto_var_init_pattern" QMAKE_PUBLIC_CONFIG)
qt_feature("stack_protector" PRIVATE
LABEL "Using -fstack-protector-strong"
AUTODETECT ON
CONDITION TEST_stack_protector
)
qt_feature_config("stack_protector" QMAKE_PUBLIC_CONFIG)
qt_feature("stack_clash_protection" PRIVATE
LABEL "Using -fstack-clash-protection"
AUTODETECT ON
CONDITION TEST_stack_clash_protection
)
qt_feature_config("stack_clash_protection" QMAKE_PUBLIC_CONFIG)
qt_feature("libstdcpp_assertions" PRIVATE
LABEL "Using libstdc++ assertions"
AUTODETECT ON
CONDITION (GCC OR (CLANG AND NOT MSVC AND NOT QT_FEATURE_stdlib_libcpp AND NOT TEST_using_stdlib_libcpp))
)
qt_feature_config("libstdcpp_assertions" QMAKE_PUBLIC_CONFIG)
qt_feature("libcpp_hardening" PRIVATE
LABEL "Using libc++ hardening"
AUTODETECT ON
CONDITION (QT_FEATURE_stdlib_libcpp OR TEST_using_stdlib_libcpp)
)
qt_feature_config("libcpp_hardening" QMAKE_PUBLIC_CONFIG)
qt_feature("relro_now_linker" PRIVATE
LABEL "Using -z relro -z now when linking"
AUTODETECT ON
CONDITION TEST_relro_now_linker
)
qt_feature_config("relro_now_linker" QMAKE_PUBLIC_CONFIG)
if("${INPUT_coverage}" STREQUAL "gcov") if("${INPUT_coverage}" STREQUAL "gcov")
qt_config_compile_test(gcov qt_config_compile_test(gcov
@ -1137,6 +1239,13 @@ qt_configure_add_summary_entry(ARGS "relocatable")
qt_configure_add_summary_entry(ARGS "precompile_header") qt_configure_add_summary_entry(ARGS "precompile_header")
qt_configure_add_summary_entry(ARGS "ltcg") qt_configure_add_summary_entry(ARGS "ltcg")
qt_configure_add_summary_entry(ARGS "intelcet") qt_configure_add_summary_entry(ARGS "intelcet")
qt_configure_add_summary_entry(ARGS "glibc_fortify_source")
qt_configure_add_summary_entry(ARGS "trivial_auto_var_init_pattern")
qt_configure_add_summary_entry(ARGS "stack_protector")
qt_configure_add_summary_entry(ARGS "stack_clash_protection")
qt_configure_add_summary_entry(ARGS "libstdcpp_assertions")
qt_configure_add_summary_entry(ARGS "libcpp_hardening")
qt_configure_add_summary_entry(ARGS "relro_now_linker")
qt_configure_add_summary_entry( qt_configure_add_summary_entry(
ARGS "wasm-simd128" ARGS "wasm-simd128"
CONDITION ( TEST_architecture_arch STREQUAL wasm ) CONDITION ( TEST_architecture_arch STREQUAL wasm )

View File

@ -76,6 +76,13 @@ qt_commandline_option(openssl-runtime TYPE void NAME openssl VALUE runtime)
qt_commandline_option(linker TYPE optionalString VALUES bfd gold lld mold) qt_commandline_option(linker TYPE optionalString VALUES bfd gold lld mold)
qt_commandline_option(ltcg TYPE boolean) qt_commandline_option(ltcg TYPE boolean)
qt_commandline_option(intelcet TYPE boolean) qt_commandline_option(intelcet TYPE boolean)
qt_commandline_option(glibc_fortify_source TYPE boolean)
qt_commandline_option(trivial_auto_var_init_pattern TYPE boolean)
qt_commandline_option(stack_protector TYPE boolean)
qt_commandline_option(stack_clash_protection TYPE boolean)
qt_commandline_option(libstdcpp_assertions TYPE boolean)
qt_commandline_option(libcpp_hardening TYPE boolean)
qt_commandline_option(relro_now_linker TYPE boolean)
qt_commandline_option(make TYPE addString VALUES examples libs tests tools qt_commandline_option(make TYPE addString VALUES examples libs tests tools
benchmarks manual-tests minimal-static-tests) benchmarks manual-tests minimal-static-tests)
qt_commandline_option(install-examples-sources TYPE boolean) qt_commandline_option(install-examples-sources TYPE boolean)