Note: This is a work in progress. Nothing to see here yet.
A C++20 project for testing and verifying compiler hardening options across multiple compilers and architectures.
This project provides a framework for systematically testing compiler-specific hardening flags and options. It supports multiple compilers and architectures with dedicated CMake configurations for each combination.
- Apple Clang - Native compiler on macOS
- Clang - LLVM Clang compiler
- GCC - GNU Compiler Collection
- MSVC - Microsoft Visual C++ Compiler
- Emscripten - For WebAssembly targets
- x86-64 - Intel/AMD 64-bit
- ARM64 - ARM 64-bit (AArch64)
- WebAssembly - Via Emscripten
- CMake 3.25 or later
- Ninja build system (recommended)
- C++20 compatible compiler
- GCC 12+ or Clang 16+
- clang-tidy (optional, for static analysis)
- Xcode Command Line Tools
- Apple Clang or LLVM Clang via Homebrew
- Visual Studio 2022 or later
- CMake and Ninja (via Visual Studio or standalone)
- Emscripten SDK (EMSDK)
- Open the Command Palette (
Cmd+Shift+PorCtrl+Shift+P) - Run: "CMake: Select Configure Preset" → Choose a preset (e.g.,
apple-clang-arm64-debug) - Run: "CMake: Configure"
- Run: "CMake: Build"
- Run tests using "CMake: Run Tests" or use the Testing sidebar
The project includes presets for all compiler/architecture combinations:
# List available presets
cmake --list-presets
# Configure with a preset
cmake --preset=<preset-name>
# Build with a preset
cmake --build --preset=<preset-name>
# Run tests
ctest --preset=<preset-name> --output-on-failure| Preset | Compiler | Architecture | Platform |
|---|---|---|---|
apple-clang-x64 |
Apple Clang | x86-64 | macOS |
apple-clang-arm64 |
Apple Clang | ARM64 | macOS |
clang-x64 |
Clang | x86-64 | Linux/macOS |
clang-arm64 |
Clang | ARM64 | Linux/macOS |
gcc-x64 |
GCC | x86-64 | Linux |
gcc-arm64 |
GCC | ARM64 | Linux |
msvc-x64 |
MSVC | x86-64 | Windows |
msvc-arm64 |
MSVC | ARM64 | Windows |
emscripten-wasm |
Emscripten | WebAssembly | Any |
The project includes a CMakeUserPresets.json.example file that can be copied to CMakeUserPresets.json to set up default presets for your system:
cp CMakeUserPresets.json.example CMakeUserPresets.jsonThis file:
- Is user-specific and not committed to git (included in
.gitignore) - Helps IDEs automatically select the appropriate preset for your platform
- Can be customized for your development environment
- Provides convenient aliases like:
default-macos-arm64/default-macos-x64(macOS)default-linux-x64-gcc/default-linux-x64-clang(Linux)default-windows-x64(Windows)
You can edit this file to set your preferred default preset based on your system.
cmake --preset=gcc-x64
cmake --build --preset=gcc-x64
ctest --test-dir build/gcc-x64 --output-on-failurecmake --preset=apple-clang-arm64-debug
cmake --build --preset=apple-clang-arm64-debug
ctest --preset=apple-clang-arm64-debug --output-on-failure# Make sure EMSDK is installed and activated
source $EMSDK/emsdk_env.sh
cmake --preset=emscripten-wasm
cmake --build --preset=emscripten-wasm
ctest --test-dir build/emscripten-wasm --output-on-failureThe CMakeLists.txt file contains a centralized apply_hardening_flags() function with compiler-specific hardening options. This function is automatically applied to all test executables, ensuring consistent hardening across the project.
To add or modify hardening flags, edit the apply_hardening_flags() function in CMakeLists.txt:
# Example: Adding hardening options for GCC
elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
message(STATUS " Compiler: GCC")
target_compile_options(${target} PRIVATE
-Wall
-Wextra
-Wpedantic
# Add your GCC hardening flags here:
-fstack-protector-strong
-D_FORTIFY_SOURCE=2
-fPIE
)
target_link_options(${target} PRIVATE
-pie
-Wl,-z,relro
-Wl,-z,now
)Each compiler section includes architecture-specific subsections:
# Architecture-specific options for GCC
if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|AMD64")
target_compile_options(${target} PRIVATE
# x86-64 specific hardening for GCC
-fcf-protection=full
)
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|ARM64")
target_compile_options(${target} PRIVATE
# ARM64 specific hardening for GCC
-mbranch-protection=standard
)
endif()To add a new test, create a source file in the test/ directory and add it to CMakeLists.txt:
# New test executable
add_executable(test-my-feature test/test_my_feature.cpp)
apply_hardening_flags(test-my-feature)
# Register with CTest
add_test(NAME my_feature COMMAND test-my-feature)
set_tests_properties(my_feature PROPERTIES LABELS "my-label")The project includes a dedicated clang-tidy preset that runs static analysis during compilation:
# Configure and build with clang-tidy enabled
cmake --preset=clang-tidy
cmake --build --preset=clang-tidyThe clang-tidy preset is based on clang-x64 and sets CMAKE_CXX_CLANG_TIDY to run clang-tidy automatically on all source files during build.
This project is test-focused. All executables are tests that verify compiler hardening features.
- Report Test (
test-report): Always passes and logs compiler/platform information - Hardening Assertions Test (
test-hardening-assertions): Validates that libc++ hardening aborts on violations on macOS
All tests are run using CTest:
# Configure with Debug mode for macOS (includes observe semantic)
cmake --preset=apple-clang-arm64-debug
# Build
cmake --build --preset=apple-clang-arm64-debug
# Run all tests
ctest --preset=apple-clang-arm64-debug --output-on-failure
# Run specific test
ctest --preset=apple-clang-arm64-debug -R report --output-on-failure| Preset | Description |
|---|---|
apple-clang-x64-debug |
Debug build with hardening enabled (macOS x86-64) |
apple-clang-arm64-debug |
Debug build with hardening enabled (macOS ARM64) |
The hardening test validates that libc++ hardening is enabled and working by intentionally triggering a violation:
- Triggers an out-of-bounds
std::spanaccess - On macOS: Test passes when program is killed by signal (SIGABRT/SIGILL), proving hardening caught the violation
- On other platforms: Test passes if it runs without crashing (may or may not have hardening)
The test runs on all platforms via CTest. On macOS, a shell wrapper detects signal termination (exit code > 128) and treats it as success.
On macOS Debug builds:
-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_DEBUG- Debug mode hardening (all checks, aborts on violations)
On macOS Release builds:
-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_FAST- Fast mode hardening (security-critical checks, aborts on violations)
See test/README.md for detailed test documentation.
The project includes GitHub Actions workflows that automatically test all compiler and architecture combinations:
- Linux: GCC 12, GCC 13, Clang 16, Clang 17
- macOS: Apple Clang on x86-64 and ARM64
- Windows: MSVC on x86-64 and ARM64
- WebAssembly: Emscripten
- Static Analysis: clang-tidy
See .github/workflows/ci.yml for details.
.
├── CMakeLists.txt # Main CMake configuration with hardening flags
├── CMakePresets.json # Presets for all compiler/arch combinations
├── CMakeUserPresets.json.example # Example user-specific default presets
├── CMakeUserPresets.json # User-specific default presets (not committed)
├── README.md # This file
├── OBSERVE_SEMANTIC_TEST.md # Implementation details for observe semantic test
├── .clang-tidy # clang-tidy configuration
├── .gitignore # Git ignore patterns
├── test/
│ ├── README.md # Test documentation
│ ├── report.cpp # Report test (logs compiler/platform info)
│ └── test_observe_assertions.cpp # Hardening assertions test (validates abort on violations)
├── tools/
│ ├── diagnostic-flags/ # Diagnostic flags analysis tools
│ └── inconsistency-analysis/ # Inconsistency analysis tools
└── .github/
└── workflows/
└── ci.yml # CI configuration
** TBD **
** TBD **