diff --git a/CMakeLists.txt b/CMakeLists.txt index 441d09af9..72fd87d22 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,7 @@ option(SLIC3R_MSVC_COMPILE_PARALLEL "Compile on Visual Studio in parallel" 1) option(SLIC3R_MSVC_PDB "Generate PDB files on MSVC in Release mode" 1) option(SLIC3R_PERL_XS "Compile XS Perl module and enable Perl unit and integration tests" 0) option(SLIC3R_ASAN "Enable ASan on Clang and GCC" 0) +option(SLIC3R_UBSAN "Enable UBSan on Clang and GCC" 0) # If SLIC3R_FHS is 1 -> SLIC3R_DESKTOP_INTEGRATION is always 0, othrewise variable. CMAKE_DEPENDENT_OPTION(SLIC3R_DESKTOP_INTEGRATION "Allow perfoming desktop integration during runtime" 1 "NOT SLIC3R_FHS" 0) @@ -271,6 +272,32 @@ if (SLIC3R_ASAN) endif () endif () +if (SLIC3R_UBSAN) + # Stacktrace for every report is enabled by default. It can be disabled by running PrusaSlicer with "UBSAN_OPTIONS=print_stacktrace=0". + + # Define macro SLIC3R_UBSAN to allow detection in the source code if this sanitizer is enabled. + add_compile_definitions(SLIC3R_UBSAN) + + # Clang supports much more useful checks than GCC, so when Clang is detected, another checks will be enabled. + # List of what GCC is checking: https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html + # List of what Clang is checking: https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html + if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + set(_ubsan_flags "-fsanitize=undefined,integer") + else () + set(_ubsan_flags "-fsanitize=undefined") + endif () + + add_compile_options(${_ubsan_flags} -fno-omit-frame-pointer) + + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${_ubsan_flags}") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${_ubsan_flags}") + set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${_ubsan_flags}") + + if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lubsan") + endif () +endif () + if (APPLE) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror=partial-availability -Werror=unguarded-availability -Werror=unguarded-availability-new") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror=partial-availability -Werror=unguarded-availability -Werror=unguarded-availability-new") diff --git a/deps/Boost/Boost.cmake b/deps/Boost/Boost.cmake index ec8bab799..15792d5a7 100644 --- a/deps/Boost/Boost.cmake +++ b/deps/Boost/Boost.cmake @@ -120,6 +120,12 @@ set(_build_cmd ${_build_cmd} set(_install_cmd ${_build_cmd} --prefix=${_prefix} install) +if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + # When Clang is used with enabled UndefinedBehaviorSanitizer, it produces "undefined reference to '__muloti4'" when __int128 is used. + # Because of that, UndefinedBehaviorSanitizer is disabled for those functions that use __int128. + list(APPEND _patch_command COMMAND ${PATCH_CMD} ${CMAKE_CURRENT_LIST_DIR}/Boost.patch) +endif () + ExternalProject_Add( dep_Boost URL "https://boostorg.jfrog.io/artifactory/main/release/1.75.0/source/boost_1_75_0.tar.gz" diff --git a/deps/Boost/Boost.patch b/deps/Boost/Boost.patch new file mode 100644 index 000000000..8c54430b9 --- /dev/null +++ b/deps/Boost/Boost.patch @@ -0,0 +1,23 @@ +diff -u ../boost_1_75_0-orig/boost/rational.hpp ./boost/rational.hpp +--- ../boost_1_75_0-orig/boost/rational.hpp 2020-12-03 06:02:19.000000000 +0100 ++++ ./boost/rational.hpp 2022-01-27 16:02:27.993848905 +0100 +@@ -302,6 +302,9 @@ + return *this; + } + template ++ #if defined(__clang__) ++ __attribute__((no_sanitize("undefined"))) ++ #endif + BOOST_CXX14_CONSTEXPR typename boost::enable_if_c::value, rational&>::type operator*= (const T& i) + { + // Avoid overflow and preserve normalization +@@ -311,6 +314,9 @@ + return *this; + } + template ++ #if defined(__clang__) ++ __attribute__((no_sanitize("undefined"))) ++ #endif + BOOST_CXX14_CONSTEXPR typename boost::enable_if_c::value, rational&>::type operator/= (const T& i) + { + // Avoid repeated construction diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp index 78e3623d9..2648fba9e 100644 --- a/src/PrusaSlicer.cpp +++ b/src/PrusaSlicer.cpp @@ -843,6 +843,15 @@ extern "C" { } #endif +#if defined(SLIC3R_UBSAN) +extern "C" { + // Enable printing stacktrace by default. It can be disabled by running PrusaSlicer with "UBSAN_OPTIONS=print_stacktrace=0". + const char *__ubsan_default_options() { + return "print_stacktrace=1"; + } +} +#endif + #if defined(_MSC_VER) || defined(__MINGW32__) extern "C" { __declspec(dllexport) int __stdcall slic3r_main(int argc, wchar_t **argv) diff --git a/src/libslic3r/Int128.hpp b/src/libslic3r/Int128.hpp index 8dc9e012d..7aeacbfce 100644 --- a/src/libslic3r/Int128.hpp +++ b/src/libslic3r/Int128.hpp @@ -105,6 +105,11 @@ public: static inline Int128 multiply(int64_t lhs, int64_t rhs) { return Int128(__int128(lhs) * __int128(rhs)); } +#if defined(__clang__) + // When Clang is used with enabled UndefinedBehaviorSanitizer, it produces "undefined reference to '__muloti4'" when __int128 is used. + // Because of that, UndefinedBehaviorSanitizer is disabled for this function. + __attribute__((no_sanitize("undefined"))) +#endif // Evaluate signum of a 2x2 determinant. static int sign_determinant_2x2(int64_t a11, int64_t a12, int64_t a21, int64_t a22) {