Compiler sanitizers
Sanitizers are tools that can detect bugs such as buffer overflows or accesses, dangling pointer or different types of undefined behavior.
The two compilers that mainly support sanitizing options are gcc and clang. These options are passed to the compiler as flags and, depending on if you are using clang or gcc, different sanitizers are supported.
Here we explain different options on how to model and use sanitizers with your Conan packages.
Adding custom settings
If you want to model the sanitizer options so that the package id is affected by them, you have to introduce new settings in the settings.yml file (see Customizing settings section for more information).
Sanitizer options should be modeled as sub-settings of the compiler. Depending on how you want to combine the sanitizers you have two choices.
Adding a list of commonly used values
If you have a fixed set of sanitizers or combinations of them that are the ones you usually set for your builds you can add the sanitizers as a list of values. An example for apple-clang would be like this:
apple-clang:
version: ["5.0", "5.1", "6.0", "6.1", "7.0", "7.3", "8.0", "8.1",
"9.0", "9.1", "10.0", "11.0"]
libcxx: [libstdc++, libc++]
cppstd: [None, 98, gnu98, 11, gnu11, 14, gnu14, 17, gnu17, 20, gnu20]
sanitizer: [None, Address, Thread, Memory, UndefinedBehavior, AddressUndefinedBehavior]
Here you have modeled the use of -fsanitize=address
, -fsanitize=thread
,
-fsanitize=memory
, -fsanitize=undefined
and the combination of -fsanitize=address
and
-fsanitize=undefined
. Note that for example, for clang it is not possible to combine more than
one of the -fsanitize=address
, -fsanitize=thread
, and -fsanitize=memory
checkers in the
same program.
Adding thread sanitizer for a conan install, in this case, could be done by calling conan install .. -s compiler.sanitizer=Thread
Adding different values to combine
Another option would be to add the sanitizer values as multiple True
or None
fields so that
they can be freely combined later. An example of that for the previous sanitizer options would be as
follows:
apple-clang:
version: ["5.0", "5.1", "6.0", "6.1", "7.0", "7.3", "8.0",
"8.1", "9.0", "9.1", "10.0", "11.0"]
libcxx: [libstdc++, libc++]
cppstd: [None, 98, gnu98, 11, gnu11, 14, gnu14, 17, gnu17, 20, gnu20]
address_sanitizer: [None, True]
thread_sanitizer: [None, True]
undefined_sanitizer: [None, True]
Then, you can add different sanitizers calling, for example, to conan install .. -s compiler.address_sanitizer=True -s compiler.undefined_sanitizer=True
A drawback of this approach is that not all the combinations will be valid or will make sense, but it is up to the consumer to use it correctly.
Passing the information to the compiler or build system
Here again, we have multiple choices to pass sanitizers information to the compiler or build system.
Using from custom profiles
It is possible to have different custom profiles defining the compiler sanitizer setting and environment variables to inject that information to the compiler, and then passing those profiles to Conan commands. An example of this would be a profile like:
[settings]
os=Macos
os_build=Macos
arch=x86_64
arch_build=x86_64
compiler=apple-clang
compiler.version=10.0
compiler.libcxx=libc++
build_type=Release
compiler.sanitizer=Address
[env]
CFLAGS=-fsanitize=address
CXXFLAGS=-fsanitize=address
LDFLAGS=-fsanitize=address
Then calling conan create . -pr address_sanitizer_profile would inject
-fsanitize=address
to the build through the CFLAGS
, CXXFLAGS
, and LDFLAGS
environment variables.
Managing sanitizer settings with the build system
Another option is to make use of the information that is propagated to the conan generator. For example, if we are using CMake we could use the information from the CMakeLists.txt to append the flags to the compiler settings like this:
cmake_minimum_required(VERSION 3.2)
project(SanitizerExample)
set (CMAKE_CXX_STANDARD 11)
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup()
set(SANITIZER ${CONAN_SETTINGS_COMPILER_SANITIZER})
if(SANITIZER)
if(SANITIZER MATCHES "(Address)")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address" )
endif()
endif()
add_executable(sanit_example src/main.cpp)
The sanitizer setting is propagated to CMake as the CONAN_SETTINGS_COMPILER_SANITIZER
variable
with a value equals to "Address"
and we can set the behavior in CMake depending on the value of
the variable.
Using conan Hooks to set compiler environment variables
Warning
This way of adding sanitizers is recommended just for testing purposes. In general, it’s not a good practice to inject this in the environment using a Conan hook. It’s much better explicitly defining this in the profiles.
Important
Take into account that the package ID doesn’t encode information about the environment, so different binaries due to different CXX_FLAGS would be considered by Conan as the same package.
If you are not interested in modelling the settings in the Conan package you can use a Hook to modify the environment variable and apply the sanitizer flags to the build. It could be something like:
import os
class SanitizerHook(object):
def __init__(self):
self._old_cxx_flags = None
def set_sanitize_address_flag(self):
self._old_cxx_flags = os.environ.get("CXXFLAGS")
flags_str = self._old_cxx_flags or ""
os.environ["CXXFLAGS"] = flags_str + " -fsanitize=address"
def reset_sanitize_address_flag(self):
if self._old_cxx_flags is None:
del os.environ["CXXFLAGS"]
else:
os.environ["CXXFLAGS"] = self._old_cxx_flags
sanitizer = SanitizerHook()
def pre_build(output, conanfile, **kwargs):
sanitizer.set_sanitize_address_flag()
def post_build(output, conanfile, **kwargs):
sanitizer.reset_sanitize_address_flag()