diff --git a/examples/security/sanitizers/compiler_sanitizers/README.md b/examples/security/sanitizers/compiler_sanitizers/README.md new file mode 100644 index 00000000..1c046b18 --- /dev/null +++ b/examples/security/sanitizers/compiler_sanitizers/README.md @@ -0,0 +1,90 @@ +# Compiler Sanitizers Example + +This example follows the documented page https://docs.conan.io/2/security/sanitizers.html about using compiler sanitizers with Conan. + +## Examples + +Here are some examples of using compiler sanitizers with Conan. + +### Configuring Custom Settings + +Before trying to build using the profiles prepared to work with sanitizers, you may want to configure some custom settings in your Conan home. +It's not needed to modify the `settings.yml` file, instead, you can install a custom settings using [settings_user.yml](https://docs.conan.io/2/reference/config_files/settings.html#settings-user-yml) + +``` +cp settings_user.yml $(conan config home) +``` + +This setting allows you to customize the behavior of the sanitizers, enabling or disabling specific checks as needed. + +### Signed Integer Overflow + +This example demonstrates how to detect signed integer overflow using compiler sanitizers. The provided C++ code intentionally causes a signed integer overflow, which can be detected when running the program with the appropriate sanitizer flags. + +It explores the [Undefined Behavior Sanitizer](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html), **ONLY** available in Clang and GCC; MSVC does not support it (yet). + +In order to try the example, you may run the following commands: + +``` +conan create signed_integer_overflow/ -pr profiles/asan_ubsan +conan build signed_integer_overflow/ --version=0.1.0 -pr profiles/asan_ubsan -of install +signed_integer_overflow/build/Debug/signed_integer_overflow +``` +It's expected to observe a runtime error indicating a signed integer overflow has occurred: + +``` +Address sanitizer not enabled +/home/conan/examples2/security/sanitizers/signed_integer_overflow/main.cpp:13:9: runtime error: signed integer overflow: 2147483647 + 1 cannot be represented in type 'int' +SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior /home/conan/examples2/security/sanitizers/signed_integer_overflow/main.cpp:13:9 +``` + +### Index Out of Bounds + +This example demonstrates how to detect out-of-bounds memory access using compiler sanitizers. The provided C++ code intentionally accesses an out-of-bounds index in an array, which can be detected when running the program with the appropriate sanitizer flags. + +It explores the [Address Sanitizer](https://clang.llvm.org/docs/AddressSanitizer.html), available in Clang, GCC and MSVC. + +In order to try the example, you may run the following commands: + +``` +conan create index_out_of_bounds/ -pr profiles/asan +conan build index_out_of_bounds/ --version=0.1.0 -pr profiles/asan -of install +index_out_of_bounds/build/Debug/index_out_of_bounds +``` + +It's expected to observe a runtime error indicating an out-of-bounds memory access has occurred: + +``` +==357155==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffcddcc40e0 at pc 0x5946a605f2eb bp 0x7ffcddcc3f10 sp 0x7ffcddcc3f00 +WRITE of size 4 at 0x7ffcddcc40e0 thread T0 + #0 0x5946a605f2ea in main (/home/conan/examples2/security/sanitizers/index_out_of_bounds/build/Debug/index_out_of_bounds) + #1 0x7722f0c29d8f in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58 + #2 0x7722f0c29e3f in __libc_start_main_impl ../csu/libc-start.c:392 + #3 0x5946a605f3d4 in _start (/home/conan/examples2/security/sanitizers/index_out_of_bounds/build/Debug/index_out_of_bounds+0x13d4) + +Address 0x7ffcddcc40e0 is located in stack of thread T0 at offset 448 in frame + #0 0x5946a605f1ef in main (/home/conan/examples2/security/sanitizers/index_out_of_bounds/build/Debug/index_out_of_bounds+0x11ef) + + This frame has 1 object(s): + [48, 448) 'foo' (line 11) <== Memory access at offset 448 overflows this variable +HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork + (longjmp and C++ exceptions *are* supported) +SUMMARY: AddressSanitizer: stack-buffer-overflow (/home/conan/examples2/security/sanitizers/index_out_of_bounds/build/Debug/index_out_of_bounds+0x12ea) in main +``` + +## Customizing Sanitizers + +### Using Environment Variables + +The `ASAN_OPTIONS` and `UBSAN_OPTIONS` environment variables can be used to customize the behavior of AddressSanitizer and UndefinedBehaviorSanitizer, respectively. For example, you can set the `ASAN_OPTIONS` variable to control the reporting format, enable or disable specific checks, and more. + +To set these environment variables, you can use the `export` command in your terminal before running your program: + +```bash +export ASAN_OPTIONS=detect_leaks=1:log_path=asan.log +export UBSAN_OPTIONS=print_stacktrace=1 +``` + +This will enable leak detection for AddressSanitizer and print stack traces for UndefinedBehaviorSanitizer. + +For more advanced configurations, you can refer to the [Clang AddressSanitizer](https://github.com/google/sanitizers/wiki/addresssanitizerflags#run-time-flags) and [MSVC AddressSanitizer](https://learn.microsoft.com/en-us/cpp/sanitizers/asan?view=msvc-170#differences) documentation. diff --git a/examples/security/sanitizers/compiler_sanitizers/ci_test_example.bat b/examples/security/sanitizers/compiler_sanitizers/ci_test_example.bat new file mode 100644 index 00000000..cb158ae7 --- /dev/null +++ b/examples/security/sanitizers/compiler_sanitizers/ci_test_example.bat @@ -0,0 +1,20 @@ +@echo off +setlocal enabledelayedexpansion + +echo Setup settings user +for /f "usebackq delims=" %%H in (conan config home) do set "CONAN_HOME=%%H" +copy /Y settings_user.yml "%CONAN_HOME%" + +echo Conan Examples 2: Compiler Sanitizers - Index Out of Bounds + +conan export index_out_of_bounds/ +conan build index_out_of_bounds/ --version=0.1.0 -pr profiles/asan -of index_out_of_bounds/install --build=missing -c tools.compilation:verbosity=verbose +index_out_of_bounds/build/Debug/index_out_of_bounds 2>nul || echo Process completed with errors (expected for sanitizer demo) + +echo Conan Examples 2: Compiler Sanitizers - Signed Integer Overflow + +conan export signed_integer_overflow/ +conan build signed_integer_overflow/ --version=0.1.0 -pr profiles/asan_ubsan -of signed_integer_overflow/install --build=missing -c tools.compilation:verbosity=verbose +signed_integer_overflow/build/Debug/signed_integer_overflow 2>nul || echo Process completed with errors (expected for sanitizer demo) + +exit /b 0 \ No newline at end of file diff --git a/examples/security/sanitizers/compiler_sanitizers/ci_test_example.sh b/examples/security/sanitizers/compiler_sanitizers/ci_test_example.sh new file mode 100755 index 00000000..b987c0b7 --- /dev/null +++ b/examples/security/sanitizers/compiler_sanitizers/ci_test_example.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +set -e +set -x + +echo "Setup settings user" +cp -f settings_user.yml $(conan config home) + +echo "Conan Examples 2: Compiler Sanitizers - Index Out of Bounds" + +conan export index_out_of_bounds/ +conan build index_out_of_bounds/ --version=0.1.0 -pr profiles/asan -of index_out_of_bounds/install --build=missing -c tools.compilation:verbosity=verbose +index_out_of_bounds/build/Debug/index_out_of_bounds || true + +echo "Conan Examples 2: Compiler Sanitizers - Signed Integer Overflow" + +conan export signed_integer_overflow/ +conan build signed_integer_overflow/ --version=0.1.0 -pr profiles/asan_ubsan -of signed_integer_overflow/install --build=missing -c tools.compilation:verbosity=verbose +signed_integer_overflow/build/Debug/signed_integer_overflow || true diff --git a/examples/security/sanitizers/compiler_sanitizers/index_out_of_bounds/CMakeLists.txt b/examples/security/sanitizers/compiler_sanitizers/index_out_of_bounds/CMakeLists.txt new file mode 100644 index 00000000..1498db2a --- /dev/null +++ b/examples/security/sanitizers/compiler_sanitizers/index_out_of_bounds/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.15) +project(index_out_of_bounds LANGUAGES CXX) + +add_executable(index_out_of_bounds main.cpp) +target_compile_features(index_out_of_bounds PUBLIC cxx_std_11) + +include(GNUInstallDirs) +install(TARGETS index_out_of_bounds + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/examples/security/sanitizers/compiler_sanitizers/index_out_of_bounds/conanfile.py b/examples/security/sanitizers/compiler_sanitizers/index_out_of_bounds/conanfile.py new file mode 100644 index 00000000..85cd00a3 --- /dev/null +++ b/examples/security/sanitizers/compiler_sanitizers/index_out_of_bounds/conanfile.py @@ -0,0 +1,28 @@ +from conan import ConanFile +from conan.tools.cmake import CMake, cmake_layout, CMakeToolchain + +required_conan_version = ">=2.1.0" + +class IndexOutOfBoundsConan(ConanFile): + name = "index_out_of_bounds" + version = "0.1.0" + settings = "os", "arch", "compiler", "build_type" + exports_sources = "CMakeLists.txt", "main.cpp" + package_type = "application" + languages = ["C++"] + + def layout(self): + cmake_layout(self) + + def generate(self): + tc = CMakeToolchain(self) + tc.generate() + + def build(self): + cmake = CMake(self) + cmake.configure() + cmake.build() + + def package(self): + cmake = CMake(self) + cmake.install() \ No newline at end of file diff --git a/examples/security/sanitizers/compiler_sanitizers/index_out_of_bounds/main.cpp b/examples/security/sanitizers/compiler_sanitizers/index_out_of_bounds/main.cpp new file mode 100644 index 00000000..8067fe62 --- /dev/null +++ b/examples/security/sanitizers/compiler_sanitizers/index_out_of_bounds/main.cpp @@ -0,0 +1,15 @@ +#include +#include + +int main() { + #ifdef __SANITIZE_ADDRESS__ + std::cout << "Address sanitizer enabled\n"; + #else + std::cout << "Address sanitizer not enabled\n"; + #endif + + int foo[100]; + foo[100] = 42; // Out-of-bounds write + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/examples/security/sanitizers/compiler_sanitizers/profiles/asan b/examples/security/sanitizers/compiler_sanitizers/profiles/asan new file mode 100644 index 00000000..7ce2d243 --- /dev/null +++ b/examples/security/sanitizers/compiler_sanitizers/profiles/asan @@ -0,0 +1,14 @@ +include(default) + +[settings] +build_type=Debug +compiler.sanitizer=Address + +[conf] +tools.build:cflags=['-fsanitize=address'] +tools.build:cxxflags=['-fsanitize=address'] +tools.build:exelinkflags=['-fsanitize=address'] +tools.build:sharedlinkflags+=["-fsanitize=address"] + +[runenv] +ASAN_OPTIONS="halt_on_error=1:detect_leaks=1" \ No newline at end of file diff --git a/examples/security/sanitizers/compiler_sanitizers/profiles/asan_ubsan b/examples/security/sanitizers/compiler_sanitizers/profiles/asan_ubsan new file mode 100644 index 00000000..0d54f55c --- /dev/null +++ b/examples/security/sanitizers/compiler_sanitizers/profiles/asan_ubsan @@ -0,0 +1,11 @@ +include(default) + +[settings] +build_type=Debug +compiler.sanitizer=AddressUndefinedBehavior + +[conf] +tools.build:cflags=['-fsanitize=address,undefined'] +tools.build:cxxflags=['-fsanitize=address,undefined'] +tools.build:exelinkflags=['-fsanitize=address,undefined'] +tools.build:sharedlinkflags+=["-fsanitize=address"] \ No newline at end of file diff --git a/examples/security/sanitizers/compiler_sanitizers/settings_user.yml b/examples/security/sanitizers/compiler_sanitizers/settings_user.yml new file mode 100644 index 00000000..c42cea1d --- /dev/null +++ b/examples/security/sanitizers/compiler_sanitizers/settings_user.yml @@ -0,0 +1,9 @@ +compiler: + gcc: + sanitizer: [null, Address, Leak, Thread, UndefinedBehavior, HardwareAssistanceAddress, KernelAddress, AddressUndefinedBehavior, ThreadUndefinedBehavior] + clang: + sanitizer: [null, Address, Leak, Thread, Memory, UndefinedBehavior, HardwareAssistanceAddress, KernelAddress, AddressUndefinedBehavior, ThreadUndefinedBehavior] + apple-clang: + sanitizer: [null, Address, Leak, Thread, Memory, UndefinedBehavior, HardwareAssistanceAddress, KernelAddress, AddressUndefinedBehavior, ThreadUndefinedBehavior] + msvc: + sanitizer: [null, Address, KernelAddress] diff --git a/examples/security/sanitizers/compiler_sanitizers/signed_integer_overflow/CMakeLists.txt b/examples/security/sanitizers/compiler_sanitizers/signed_integer_overflow/CMakeLists.txt new file mode 100644 index 00000000..73687d17 --- /dev/null +++ b/examples/security/sanitizers/compiler_sanitizers/signed_integer_overflow/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.15) +project(signed_integer_overflow LANGUAGES CXX) + +add_executable(signed_integer_overflow main.cpp) +target_compile_features(signed_integer_overflow PUBLIC cxx_std_11) + +include(GNUInstallDirs) +install(TARGETS signed_integer_overflow + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/examples/security/sanitizers/compiler_sanitizers/signed_integer_overflow/conanfile.py b/examples/security/sanitizers/compiler_sanitizers/signed_integer_overflow/conanfile.py new file mode 100644 index 00000000..c7c85802 --- /dev/null +++ b/examples/security/sanitizers/compiler_sanitizers/signed_integer_overflow/conanfile.py @@ -0,0 +1,28 @@ +from conan import ConanFile +from conan.tools.cmake import CMake, cmake_layout, CMakeToolchain + +required_conan_version = ">=2.1.0" + +class SignedIntegerOverflowConan(ConanFile): + name = "signed_integer_overflow" + version = "0.1.0" + settings = "os", "arch", "compiler", "build_type" + exports_sources = "CMakeLists.txt", "main.cpp" + package_type = "application" + languages = ["C++"] + + def layout(self): + cmake_layout(self) + + def generate(self): + tc = CMakeToolchain(self) + tc.generate() + + def build(self): + cmake = CMake(self) + cmake.configure() + cmake.build() + + def package(self): + cmake = CMake(self) + cmake.install() \ No newline at end of file diff --git a/examples/security/sanitizers/compiler_sanitizers/signed_integer_overflow/main.cpp b/examples/security/sanitizers/compiler_sanitizers/signed_integer_overflow/main.cpp new file mode 100644 index 00000000..52a5eac5 --- /dev/null +++ b/examples/security/sanitizers/compiler_sanitizers/signed_integer_overflow/main.cpp @@ -0,0 +1,16 @@ +#include +#include +#include + +int main(int argc, char* argv[]) { + #ifdef __SANITIZE_ADDRESS__ + std::cout << "Address sanitizer enabled\n"; + #else + std::cout << "Address sanitizer not enabled\n"; + #endif + + int foo = 0x7fffffff; + foo += argc; // Signed integer overflow + + return EXIT_SUCCESS; +} \ No newline at end of file