Kaynağa Gözat

Add sanitizers

Signed-off-by: Martin Melik Merkumians <martin.melik@gmail.com>
Martin Melik Merkumians 2 hafta önce
ebeveyn
işleme
10b7950c87

+ 3 - 3
source/CMakeLists.txt

@@ -129,13 +129,13 @@ if (DOXYGEN_FOUND)
 
     # exclude subdirectories of non active platforms depending on OpENer_PLATFORM
     set(OpENer_EXCLUDE_PATTERNS "")
-    if (NOT (${OpENer_PLATFORM} STREQUAL "POSIX"))
+    if (NOT OpENer_PLATFORM STREQUAL "POSIX")
         set(OpENer_EXCLUDE_PATTERNS "${OpENer_EXCLUDE_PATTERNS} */src/ports/POSIX/*")
     endif ()
-    if (NOT (${OpENer_PLATFORM} STREQUAL "WIN32"))
+    if (NOT OpENer_PLATFORM STREQUAL "WIN32")
         set(OpENer_EXCLUDE_PATTERNS "${OpENer_EXCLUDE_PATTERNS} */src/ports/WIN32/*")
     endif ()
-    if (NOT (${OpENer_PLATFORM} STREQUAL "MINGW"))
+    if (NOT OpENer_PLATFORM STREQUAL "MINGW")
         set(OpENer_EXCLUDE_PATTERNS "${OpENer_EXCLUDE_PATTERNS} */src/ports/MINGW/*")
     endif ()
 

+ 2 - 2
source/buildsupport/CodeCoverage.cmake

@@ -277,13 +277,13 @@ function(setup_target_for_coverage_lcov)
 
     # Show where to find the lcov info report
     add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
-        COMMAND ;
+        COMMAND ${CMAKE_COMMAND} -E echo "Lcov code coverage info report saved in ${Coverage_NAME}.info."
         COMMENT "Lcov code coverage info report saved in ${Coverage_NAME}.info."
     )
 
     # Show info where to find the report
     add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
-        COMMAND ;
+        COMMAND ${CMAKE_COMMAND} -E echo "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report."
         COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report."
     )
 

+ 102 - 0
source/buildsupport/OpENer.cmake

@@ -73,3 +73,105 @@ macro(createTraceLevelOptions)
 
   add_definitions(-DOPENER_TRACE_LEVEL=${TRACE_LEVEL})
 endmacro(createTraceLevelOptions)
+
+#######################################
+# Build hardening, sanitizers, coverage
+#######################################
+
+# Easy toggles for sanitizers, hardening, coverage and warnings
+option(OpENer_ENABLE_ASAN "Enable AddressSanitizer" OFF)
+option(OpENer_ENABLE_UBSAN "Enable UndefinedBehaviorSanitizer" OFF)
+option(OpENer_ENABLE_TSAN "Enable ThreadSanitizer" OFF)
+option(OpENer_ENABLE_MSAN "Enable MemorySanitizer (Clang only)" OFF)
+option(OpENer_ENABLE_LSAN "Enable LeakSanitizer" OFF)
+option(OpENer_ENABLE_COVERAGE "Enable code coverage flags" OFF)
+option(OpENer_ENABLE_HARDENING "Enable hardening flags (FORTIFY, PIE, RELRO, stack protector)" OFF)
+option(OpENer_ENABLE_STRICT_WARNINGS "Enable comprehensive warnings" OFF)
+
+function(opener_apply_build_options)
+  # Compose sanitizer list
+  set(_sanitizers "")
+  if(OpENer_ENABLE_ASAN)
+    list(APPEND _sanitizers address)
+  endif()
+  if(OpENer_ENABLE_UBSAN)
+    list(APPEND _sanitizers undefined)
+  endif()
+  if(OpENer_ENABLE_TSAN)
+    list(APPEND _sanitizers thread)
+  endif()
+  if(OpENer_ENABLE_MSAN)
+    list(APPEND _sanitizers memory)
+  endif()
+  if(OpENer_ENABLE_LSAN)
+    list(APPEND _sanitizers leak)
+  endif()
+
+  if(_sanitizers)
+    string(REPLACE ";" "," _sanitizer_string "${_sanitizers}")
+    # Apply to compile and link flags only for enabled languages
+    foreach(lang C CXX)
+      # Skip languages that haven't been enabled
+      if(NOT DEFINED CMAKE_${lang}_COMPILER_ID)
+        continue()
+      endif()
+      if(CMAKE_${lang}_COMPILER_ID MATCHES "Clang" OR CMAKE_${lang}_COMPILER_ID MATCHES "GNU")
+        set(var_cflags "${CMAKE_${lang}_FLAGS}")
+        set(var_cflags "${var_cflags} -fsanitize=${_sanitizer_string} -fno-omit-frame-pointer")
+        set(CMAKE_${lang}_FLAGS "${var_cflags}")
+        set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=${_sanitizer_string}")
+      else()
+        message(WARNING "Sanitizers requested but unsupported compiler: ${CMAKE_${lang}_COMPILER_ID}")
+      endif()
+    endforeach()
+    # UBSan: add recover behavior to aid debugging if only UBSan requested
+    if(OpENer_ENABLE_UBSAN)
+      add_compile_options(-fno-sanitize-recover=undefined)
+    endif()
+  endif()
+
+  if(OpENer_ENABLE_COVERAGE)
+    if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "")
+      message(STATUS "Enabling code coverage flags")
+      foreach(lang C CXX)
+        set(CMAKE_${lang}_FLAGS "${CMAKE_${lang}_FLAGS} -g -O0 --coverage")
+      endforeach()
+      set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage")
+    else()
+      message(WARNING "Coverage requested but build type is not Debug; consider using -DCMAKE_BUILD_TYPE=Debug")
+    endif()
+  endif()
+
+  if(OpENer_ENABLE_HARDENING)
+    message(STATUS "Enabling hardening/fortify flags")
+    add_compile_options(-fstack-protector-strong -D_FORTIFY_SOURCE=2)
+    # PIE and RELRO
+    add_compile_options(-fPIE)
+    set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,relro -Wl,-z,now -pie")
+  endif()
+
+  if(OpENer_ENABLE_STRICT_WARNINGS)
+    message(STATUS "Enabling strict warning flags")
+    add_compile_options(-Wall -Wextra -Wpedantic -Wconversion -Wshadow -Wdouble-promotion -Wformat=2)
+  endif()
+endfunction(opener_apply_build_options)
+
+## If this is a Debug or CI/Test build, enable a sensible default set of
+## sanitizers, coverage, hardening and strict warnings to make CI runs
+## more effective. This will apply when the build type is Debug, tests are
+## enabled (OpENer_TESTS) or the CI environment variable is present. These
+## cache variables are forced here so CI scripts that don't pass flags still
+## get the helpful defaults.
+if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR OpENer_TESTS OR DEFINED ENV{CI})
+  message(STATUS "CI/Debug/Test detected: enabling sanitizer/coverage/hardening defaults")
+  set(OpENer_ENABLE_ASAN     ON CACHE BOOL "Enable AddressSanitizer" FORCE)
+  set(OpENer_ENABLE_UBSAN    ON CACHE BOOL "Enable UndefinedBehaviorSanitizer" FORCE)
+  set(OpENer_ENABLE_LSAN     ON CACHE BOOL "Enable LeakSanitizer" FORCE)
+  set(OpENer_ENABLE_COVERAGE ON CACHE BOOL "Enable code coverage flags" FORCE)
+  set(OpENer_ENABLE_HARDENING ON CACHE BOOL "Enable hardening flags (FORTIFY, PIE, RELRO, stack protector)" FORCE)
+  set(OpENer_ENABLE_STRICT_WARNINGS ON CACHE BOOL "Enable comprehensive warnings" FORCE)
+  # MSAN and TSAN remain opt-in only due to toolchain/runtime requirements
+endif()
+
+# Apply options immediately so callers don't need to remember to invoke the function
+opener_apply_build_options()

+ 3 - 3
source/src/cip/cipidentity.c

@@ -106,7 +106,7 @@ CipShortString* GetDeviceProductName(void) {
 }
 
 static inline void MergeStatusAndExtStatus(void) {
-  CipWord status_flags = g_identity.status & (~kExtStatusMask);
+  CipWord status_flags = g_identity.status & (CipWord) ~(CipWord)kExtStatusMask;
   CipWord ext_status   = g_identity.ext_status & kExtStatusMask;
 
   // Any major fault will override the current extended status with kMajorFault.
@@ -127,7 +127,7 @@ static inline void MergeStatusAndExtStatus(void) {
  *  value.
  */
 void CipIdentitySetStatusFlags(const CipWord status_flags) {
-  g_identity.status |= status_flags & (~kExtStatusMask);
+  g_identity.status |= status_flags & (CipWord) ~(CipWord)kExtStatusMask;
   MergeStatusAndExtStatus();
 }
 
@@ -140,7 +140,7 @@ void CipIdentitySetStatusFlags(const CipWord status_flags) {
  *  value.
  */
 void CipIdentityClearStatusFlags(const CipWord status_flags) {
-  g_identity.status &= ~(status_flags & (~kExtStatusMask));
+  g_identity.status &= status_flags & (CipWord) ~(CipWord)kExtStatusMask;
   MergeStatusAndExtStatus();
 }