Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions cmake/modules/FindASan.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
include_guard(GLOBAL)

option(MORPHEUS_ADDRESS_SANITIZER "Enable AddressSanitizer for sanitized targets." OFF)

if (NOT MORPHEUS_ADDRESS_SANITIZER)
return()
endif()

if (MORPHEUS_ADDRESS_SANITIZER AND (MORPHEUS_MEMORY_SANITIZER OR MORPHEUS_THREAD_SANITIZER))
message(FATAL_ERROR "morpheus: AddressSanitizer is not compatible with ThreadSanitizer or MemorySanitizer.")
endif ()

# For gcc and clang first attempt to sanitize address with full frame pointer information.
list(APPEND asanCandidateFlag "-g -fsanitize=address -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer")
list(APPEND asanCandidateFlag "-g -fsanitize=address -fno-omit-frame-pointer")
list(APPEND asanCandidateFlag "-g -fsanitize=address")
list(APPEND asanCandidateFlag "/fsanitize=address") # MSVC syntax

include(morpheus_check_compiler_flags)
morpheus_check_compiler_flags(
NAME "Address Sanitizer"
PREFIX ASan
FLAGS ${asanCandidateFlag}
RESULT asanSupported
)

find_package_handle_standard_args(ASan REQUIRED_VARS asanSupported)

if (ASan_FOUND AND NOT TARGET ASan::ASan)
add_library(ASan INTERFACE)
add_library(morpheus::ASan ALIAS ASan)
target_compile_options(ASan
INTERFACE

)
endif()

29 changes: 29 additions & 0 deletions cmake/modules/FindMSan.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
include_guard(GLOBAL)

option(MORPHEUS_MEMORY_SANITIZER "Enable MemorySanitizer for sanitized targets." OFF)

if (NOT MORPHEUS_MEMORY_SANITIZER)
return()
endif()

list(APPEND msanCandidateFlag "-g -fsanitize=memory") # Gcc & Clang syntax
list(APPEND msanCandidateFlag "/fsanitize=memory") # MSVC syntax

include(morpheus_check_compiler_flags)
morpheus_check_compiler_flags(
NAME "Thread Sanitizer"
PREFIX MSan
FLAGS ${msanCandidateFlag}
RESULT msanSupported
)

find_package_handle_standard_args(TSan REQUIRED_VARS msanSupported)

if (MSan_FOUND AND NOT TARGET morpheus::MSan)
add_library(MSan INTERFACE)
add_library(morpheus::MSan ALIAS MSan)
target_compile_options(MSan
INTERFACE
#msanCandidateFlag
)
endif()
1 change: 1 addition & 0 deletions cmake/modules/FindSanitizers.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

29 changes: 29 additions & 0 deletions cmake/modules/FindTSan.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
include_guard(GLOBAL)

option(MORPHEUS_THREAD_SANITIZER "Enable ThreadSanitizer for sanitized targets." OFF)

if (NOT MORPHEUS_THREAD_SANITIZER)
return()
endif()

list(APPEND tsanCandidateFlag "-g -fsanitize=thread") # Gcc & Clang syntax
list(APPEND tsanCandidateFlag "/fsanitize=thread") # MSVC syntax

include(morpheus_check_compiler_flags)
morpheus_check_compiler_flags(
NAME "Thread Sanitizer"
PREFIX TSan
FLAGS ${tsanCandidateFlag}
RESULT tsanSupported
)

find_package_handle_standard_args(TSan REQUIRED_VARS tsanSupported)

if (TSan_FOUND AND NOT TARGET morpheus::TSan)
add_library(TSan INTERFACE)
add_library(morpheus::TSan ALIAS TSan)
target_compile_options(TSan
INTERFACE
#tsanCandidateFlag
)
endif()
29 changes: 29 additions & 0 deletions cmake/modules/FindUBSan.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
include_guard(GLOBAL)

option(MORPHEUS_UNDEFINED_BEHAVIOUR_SANITIZER "Enable UndefineBehaviourSanitizer for sanitized targets." OFF)

if (NOT MORPHEUS_UNDEFINED_BEHAVIOUR_SANITIZER)
return()
endif()

list(APPEND ubsanCandidateFlag "-g -fsanitize=undefined") # Gcc & Clang syntax
list(APPEND ubsanCandidateFlag "/fsanitize=undefined") # MSVC syntax

include(morpheus_check_compiler_flags)
morpheus_check_compiler_flags(
NAME "Undefined behavior Sanitizer"
PREFIX UBSan
FLAGS ${ubsanCandidateFlag}
RESULT ubsanSupported
)

find_package_handle_standard_args(UBSan REQUIRED_VARS ubsanSupported)

if (UBSan_FOUND AND NOT TARGET morpheus::UBSan)
add_library(UBSan INTERFACE)
add_library(morpheus::UBSan ALIAS UBSan)
target_compile_options(UBSan
INTERFACE
#ubsanCandidateFlag
)
endif()
151 changes: 151 additions & 0 deletions cmake/morpheus_check_compiler_flags.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
include_guard(GLOBAL)

cmake_minimum_required(VERSION 3.25)

#[=======================================================================[.rst:
morpheus_check_compiler_flag
------------------

Overview
^^^^^^^^

Checks for the given compiler if the given flag is supported for the specified languages
if CMake provides a compiler check module for the underlying language.

.. code-block:: cmake

morpheus_check_compiler_flag(
[FLAG <flag>]
[LANGUAGE <langauge>]
[RESULT <result>]
)
-- Confirms support for the compile flag across enabled languages.

``FLAG``
The ``FLAG`` compiler flag to test if is supported for the specified language.

``RESULT``
The ``RESULT`` option is required to store the result of the function. TRUE if
there is compiler support for the feature.

#]=======================================================================]
function(morpheus_check_compiler_flag)
set(options)
set(oneValueArgs FLAG LANGUAGE RESULT)
set(multiValueArgs)
cmake_parse_arguments(MORPHEUS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})

if (NOT MORPHEUS_FLAG)
message(FATAL_ERROR "FLAG parameter must be supplied")
endif()
if (NOT MORPHEUS_LANGUAGE)
message(FATAL_ERROR "LANGUAGE parameter must be supplied")
endif()
if (NOT MORPHEUS_RESULT)
message(FATAL_ERROR "RESULT parameter must be supplied")
endif()

if (${MORPHEUS_LANGUAGE} STREQUAL "C")
include(CheckCCompilerFlag)
check_c_compiler_flag("${MORPHEUS_FLAG}" ${MORPHEUS_RESULT})
elseif (${MORPHEUS_LANGUAGE} STREQUAL "CXX")
include(CheckCXXCompilerFlag)
check_cxx_compiler_flag("${MORPHEUS_FLAG}" ${MORPHEUS_RESULT})
elseif (${MORPHEUS_LANGUAGE} STREQUAL "Fortran")
include(CheckFortranCompilerFlag)
check_fortran_compiler_flag("${MORPHEUS_FLAG}" ${MORPHEUS_RESULT})
elseif (${MORPHEUS_LANGUAGE} STREQUAL "OBJC")
include(CheckOBJCCompilerFlag)
check_objc_compiler_flag("${MORPHEUS_FLAG}" ${MORPHEUS_RESULT})
elseif (${MORPHEUS_LANGUAGE} STREQUAL "OBJCXX")
include(CheckOBJCXXCompilerFlag)
check_objcxx_compiler_flag("${MORPHEUS_FLAG}" ${MORPHEUS_RESULT})
else()
set(${MORPHEUS_RESULT} FALSE)
return(PROPAGATE ${MORPHEUS_RESULT})
endif()
endfunction()


#[=======================================================================[.rst:
morpheus_check_compiler_flags
------------------

Overview
^^^^^^^^

Checks for the given compile supported of the given flags across all languages in
use which have compiler check modules provides in CMake.

.. code-block:: cmake

morpheus_check_compiler_flags(
[QUIET]
[FLAGS <flags>]
[RESULT <result>]
)
-- Confirms support for compiler flags across enabled languages.

``RESULT``
The ``RESULT`` option is required to store the results of the function. TRUE if
there is compiler support for the feature.

``FLAGS``
The ``FLAGS`` compiler flags to test if are supported for the enabled languages.

#]=======================================================================]
function(morpheus_check_compiler_flags)
set(options QUIET)
set(oneValueArgs NAME PREFIX RESULT)
set(multiValueArgs FLAGS)
cmake_parse_arguments(MORPHEUS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if (NOT MORPHEUS_NAME)
message(FATAL_ERROR "NAME parameter must be supplied")
endif()
if (NOT MORPHEUS_PREFIX)
message(FATAL_ERROR "PREFIX parameter must be supplied")
endif()
if (NOT MORPHEUS_RESULT)
message(FATAL_ERROR "RESULT parameter must be supplied")
endif()
if (NOT MORPHEUS_FLAGS)
message(FATAL_ERROR "FLAGS parameter must be supplied")
endif()

get_property(languages GLOBAL PROPERTY ENABLED_LANGUAGES)

foreach(language IN LISTS languages)

set(COMPILER ${CMAKE_${language}_COMPILER_ID})
if (${MORPHEUS_PREFIX}_${COMPILER}_FLAGS)
continue()
endif()

foreach(flags IN LISTS MORPHEUS_FLAGS)
if (NOT MORPHEUS_QUIET)
message(STATUS "Try ${COMPILER} ${MORPHEUS_NAME} flag = [${flags}]")
endif()

set(CMAKE_REQUIRED_FLAGS "${flags}")
unset(flagsSupported CACHE)
morpheus_check_compiler_flag(FLAG "${flags}" LANGUAGE ${language} RESULT flagsSupported)

if (flagsSupported)
set(${MORPHEUS_PREFIX}_${COMPILER}_FLAGS "${flags}" CACHE STRING "${MORPHEUS_NAME} flags for ${COMPILER} compiler.")
mark_as_advanced(${MORPHEUS_PREFIX}_${COMPILER}_FLAGS)
break()
endif()
endforeach()

if (NOT ${flagsSupported})
set(${MORPHEUS_PREFIX}_${COMPILER}_FLAGS "" CACHE STRING "${MORPHEUS_NAME} flags for ${COMPILER} compiler.")
mark_as_advanced(${MORPHEUS_PREFIX}_${COMPILER}_FLAGS)
message(WARNING "${MORPHEUS_NAME} is not available for ${COMPILER} compiler. Targets using "
"this compiler will be compiled without ${MORPHEUS_NAME}.")
endif()
endforeach()

set(${MORPHEUS_RESULT} FALSE)
return(PROPAGATE ${MORPHEUS_RESULT})

endfunction()
11 changes: 8 additions & 3 deletions cmake/sanitisers.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ include_guard(GLOBAL)

include(targets)

#[[
option(ENABLE_ADDRESS_SANITIZER "Enable AddressSanitizer for sanitized targets." OFF)
option(ENABLE_MEMORY_SANITIZER "Enable MemorySanitizer for sanitized targets." OFF)
option(ENABLE_THREAD_SANITIZER "Enable ThreadSanitizer for sanitized targets." OFF)
Expand All @@ -27,7 +28,8 @@ option(ENABLE_UNDEFINED_BEHAVIOUR_SANITIZER "Enable UndefineBehaviourSanitizer f
if(NOT (ENABLE_ADDRESS_SANITIZER OR ENABLE_MEMORY_SANITIZER OR ENABLE_THREAD_SANITIZER OR ENABLE_UNDEFINED_BEHAVIOUR_SANITIZER))
return()
endif()

]]
#[[
FetchContent_Declare(
sanitizers
GIT_REPOSITORY https://github.com/arsenm/sanitizers-cmake
Expand All @@ -37,13 +39,16 @@ if(NOT sanitizers_POPULATED)
FetchContent_Populate(sanitizers)
list(APPEND CMAKE_MODULE_PATH ${sanitizers_SOURCE_DIR}/cmake)
endif()
]]

set(SANITIZE_ADDRESS ${ENABLE_ADDRESS_SANITIZER} CACHE BOOL "Enable AddressSanitizer for sanitized targets." FORCE)
set(SANITIZE_MEMORY ${ENABLE_MEMORY_SANITIZER} CACHE BOOL "Enable MemorySanitizer for sanitized targets." FORCE)
set(SANITIZE_THREAD ${ENABLE_THREAD_SANITIZER} CACHE BOOL "Enable ThreadSanitizer for sanitized targets." FORCE)
set(SANITIZE_UNDEFINED ${ENABLE_UNDEFINED_BEHAVIOUR_SANITIZER} CACHE BOOL "Enable UndefinedBehaviorSanitizer for sanitized targets." FORCE)
find_package(Sanitizers)
#find_package(Sanitizers)

find_package(ASan)

targets_get_all(RESULT allTargets DIRECTORY ${PROJECT_SOURCE_DIR})
targets_filter_for_sources(RESULT targetsWithSource TARGETS ${allTargets})
add_sanitizers("${targetsWithSource}")
#add_sanitizers("${targetsWithSource}")
2 changes: 1 addition & 1 deletion libraries/core/src/morpheus/core/base/sanitizers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,4 @@ namespace mopheus
#define MORPHEUS_NO_SANITIZE_ADDRESS
#endif

} // namespace mopheus
} // namespace morpheus
10 changes: 10 additions & 0 deletions libraries/core/tests/cmake/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
morpheus_add_tests(
NAME MorpheusAsanTests
FOLDER "Libraries/Core"
)

find_package(ASan REQUIRED)
target_sources(MorpheusAsanTests
PUBLIC
asan.tests.cpp
)
18 changes: 18 additions & 0 deletions libraries/core/tests/cmake/asan.tests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#include <catch2/catch_all.hpp>

#include <memory>
#include <functional>

namespace morpheus
{

TEST_CASE("Ensure the address sanitizer detects unsafe use after free memory usage", "[quantbox.cmake.asan]")
{
auto storage = std::make_unique<int>(1);
std::reference_wrapper<int> dangling(*storage);
storage.release();
REQUIRE(!storage);
dangling.get() = 0; // If asan works this is an error!
}

} // morpheus