@@ -11,89 +11,112 @@ function(
1111 ENABLE_SANITIZER_UNDEFINED_BEHAVIOR
1212 ENABLE_SANITIZER_THREAD
1313 ENABLE_SANITIZER_MEMORY
14+ ENABLE_SANITIZER_POINTER_COMPARE
15+ ENABLE_SANITIZER_POINTER_SUBTRACT
1416)
1517
16- if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES ".*Clang" )
17- set (SANITIZERS "" )
18-
19- if (${ENABLE_SANITIZER_ADDRESS} )
20- list (APPEND SANITIZERS "address" )
21- if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 8)
22- list (APPEND SANITIZERS "pointer-compare" "pointer-subtract" )
23- message (
24- STATUS
25- "To enable invalid pointer pairs detection, add detect_invalid_pointer_pairs=2 to the environment variable ASAN_OPTIONS."
26- )
18+ # check if the sanitizers are supported
19+ check_sanitizers_support(
20+ SUPPORTS_SANITIZER_ADDRESS
21+ SUPPORTS_SANITIZER_UNDEFINED_BEHAVIOR
22+ SUPPORTS_SANITIZER_LEAK
23+ SUPPORTS_SANITIZER_THREAD
24+ SUPPORTS_SANITIZER_MEMORY
25+ SUPPORTS_SANITIZER_POINTER_COMPARE
26+ SUPPORTS_SANITIZER_POINTER_SUBTRACT
27+ )
28+
29+ # for each sanitizer, check if it is supported and enabled
30+ set (SANITIZERS "" )
31+ foreach (
32+ SANITIZER IN
33+ ITEMS "address"
34+ "leak"
35+ "undefined"
36+ "thread"
37+ "memory"
38+ "pointer-compare"
39+ "pointer-subtract"
40+ )
41+ if (${ENABLE_SANITIZER_${SANITIZER} })
42+ if (${SUPPORTS_SANITIZER_${SANITIZER} })
43+ list (APPEND SANITIZERS ${SANITIZER} )
44+ else ()
45+ # do not enable the sanitizer if it is not supported
46+ message (STATUS "${SANITIZER} sanitizer is not supported. Not enabling it." )
2747 endif ()
2848 endif ()
49+ endforeach ()
2950
30- if (${ENABLE_SANITIZER_LEAK} )
31- list (APPEND SANITIZERS "leak" )
32- endif ()
51+ # Info on special cases
3352
34- if (${ENABLE_SANITIZER_UNDEFINED_BEHAVIOR} )
35- list (APPEND SANITIZERS "undefined" )
36- endif ()
37-
38- if (${ENABLE_SANITIZER_THREAD} )
39- if ("address" IN_LIST SANITIZERS OR "leak" IN_LIST SANITIZERS)
40- message (WARNING "Thread sanitizer does not work with Address and Leak sanitizer enabled" )
41- else ()
42- list (APPEND SANITIZERS "thread" )
43- endif ()
53+ # Address sanitizer requires Leak sanitizer to be disabled
54+ if (${ENABLE_SANITIZER_THREAD} AND "${SUPPORTS_SANITIZER_THREAD} " STREQUAL "ENABLE_SANITIZER_THREAD" )
55+ if ("address" IN_LIST SANITIZERS OR "leak" IN_LIST SANITIZERS)
56+ message (
57+ WARNING
58+ "Thread sanitizer does not work with Address or Leak sanitizer enabled. Disabling the thread sanitizer."
59+ )
60+ # remove thread sanitizer from the list
61+ list (REMOVE_ITEM SANITIZERS "thread" )
4462 endif ()
63+ endif ()
4564
46- if (${ENABLE_SANITIZER_MEMORY} AND CMAKE_CXX_COMPILER_ID MATCHES ".*Clang" )
65+ # Memory sanitizer requires all the code (including libc++) to be MSan-instrumented otherwise it reports false positives
66+ if (${ENABLE_SANITIZER_MEMORY} AND "${SUPPORTS_SANITIZER_MEMORY} " STREQUAL "ENABLE_SANITIZER_MEMORY"
67+ AND CMAKE_CXX_COMPILER_ID MATCHES ".*Clang"
68+ )
69+ message (
70+ STATUS
71+ "Memory sanitizer requires all the code (including libc++) to be MSan-instrumented otherwise it reports false positives"
72+ )
73+ if ("address" IN_LIST SANITIZERS OR "thread" IN_LIST SANITIZERS OR "leak" IN_LIST SANITIZERS)
4774 message (
4875 WARNING
49- "Memory sanitizer requires all the code (including libc++) to be MSan-instrumented otherwise it reports false positives "
76+ "Memory sanitizer does not work with Address, Thread and Leak sanitizer enabled. Disabling the memory sanitizer. "
5077 )
51- if ("address" IN_LIST SANITIZERS OR "thread" IN_LIST SANITIZERS OR "leak" IN_LIST SANITIZERS)
52- message (WARNING "Memory sanitizer does not work with Address, Thread and Leak sanitizer enabled" )
53- else ()
54- list (APPEND SANITIZERS "memory" )
55- endif ()
56- endif ()
57- elseif (MSVC )
58- if (${ENABLE_SANITIZER_ADDRESS} )
59- list (APPEND SANITIZERS "address" )
78+ # remove memory sanitizer from the list
79+ list (REMOVE_ITEM SANITIZERS "memory" )
6080 endif ()
61- if (${ENABLE_SANITIZER_LEAK}
62- OR ${ENABLE_SANITIZER_UNDEFINED_BEHAVIOR}
63- OR ${ENABLE_SANITIZER_THREAD}
64- OR ${ENABLE_SANITIZER_MEMORY}
81+ endif ()
82+
83+ if ((${ENABLE_SANITIZER_POINTER_COMPARE} AND "${SUPPORTS_SANITIZER_POINTER_COMPARE} " STREQUAL
84+ "ENABLE_SANITIZER_POINTER_COMPARE" )
85+ OR (${ENABLE_SANITIZER_POINTER_SUBTRACT} AND "${SUPPORTS_SANITIZER_POINTER_SUBTRACT} " STREQUAL
86+ "ENABLE_SANITIZER_POINTER_SUBTRACT" )
87+ )
88+ message (
89+ STATUS
90+ "To enable invalid pointer pairs detection, add detect_invalid_pointer_pairs=2 to the environment variable ASAN_OPTIONS."
6591 )
66- message (WARNING "MSVC only supports address sanitizer" )
67- endif ()
6892 endif ()
6993
94+ # Join the sanitizers
7095 list (JOIN SANITIZERS "," LIST_OF_SANITIZERS)
7196
72- if (LIST_OF_SANITIZERS)
73- if (NOT "${LIST_OF_SANITIZERS} " STREQUAL "" )
74- if (NOT MSVC )
75- target_compile_options (${_project_name} INTERFACE -fsanitize=${LIST_OF_SANITIZERS} )
76- target_link_options (${_project_name} INTERFACE -fsanitize=${LIST_OF_SANITIZERS} )
77- else ()
78- string (FIND "$ENV{PATH} " "$ENV{VSINSTALLDIR} " index_of_vs_install_dir)
79- if ("${index_of_vs_install_dir} " STREQUAL "-1" )
80- message (
81- SEND_ERROR
82- "Using MSVC sanitizers requires setting the MSVC environment before building the project. Please manually open the MSVC command prompt and rebuild the project."
83- )
84- endif ()
85- if (POLICY CMP0141)
86- if ("${CMAKE_MSVC_DEBUG_INFORMATION_FORMAT} " STREQUAL "" OR "${CMAKE_MSVC_DEBUG_INFORMATION_FORMAT} "
87- STREQUAL "EditAndContinue"
88- )
89- set_target_properties (${_project_name} PROPERTIES MSVC_DEBUG_INFORMATION_FORMAT ProgramDatabase)
90- endif ()
91- else ()
92- target_compile_options (${_project_name} INTERFACE /Zi)
97+ if (LIST_OF_SANITIZERS AND NOT "${LIST_OF_SANITIZERS} " STREQUAL "" )
98+ if (NOT MSVC )
99+ target_compile_options (${_project_name} INTERFACE -fsanitize=${LIST_OF_SANITIZERS} )
100+ target_link_options (${_project_name} INTERFACE -fsanitize=${LIST_OF_SANITIZERS} )
101+ else ()
102+ string (FIND "$ENV{PATH} " "$ENV{VSINSTALLDIR} " index_of_vs_install_dir)
103+ if ("${index_of_vs_install_dir} " STREQUAL "-1" )
104+ message (
105+ SEND_ERROR
106+ "Using MSVC sanitizers requires setting the MSVC environment before building the project. Please manually open the MSVC command prompt and rebuild the project."
107+ )
108+ endif ()
109+ if (POLICY CMP0141)
110+ if ("${CMAKE_MSVC_DEBUG_INFORMATION_FORMAT} " STREQUAL "" OR "${CMAKE_MSVC_DEBUG_INFORMATION_FORMAT} "
111+ STREQUAL "EditAndContinue"
112+ )
113+ set_target_properties (${_project_name} PROPERTIES MSVC_DEBUG_INFORMATION_FORMAT ProgramDatabase)
93114 endif ()
94- target_compile_options ( ${_project_name} INTERFACE /fsanitize= ${LIST_OF_SANITIZERS} /INCREMENTAL: NO )
95- target_link_options (${_project_name} INTERFACE /INCREMENTAL: NO )
115+ else ( )
116+ target_compile_options (${_project_name} INTERFACE /Zi )
96117 endif ()
118+ target_compile_options (${_project_name} INTERFACE /fsanitize=${LIST_OF_SANITIZERS} /INCREMENTAL:NO )
119+ target_link_options (${_project_name} INTERFACE /INCREMENTAL:NO )
97120 endif ()
98121 endif ()
99122
@@ -104,7 +127,7 @@ endfunction()
104127``check_sanitizers_support``
105128===============
106129
107- Detect sanitizers support for compiler.
130+ Detect sanitizers support for compiler. You don't need to call this function directly anymore.
108131
109132Note that some sanitizers cannot be enabled together, and this function doesn't check that. You should decide which sanitizers to enable based on your needs.
110133
@@ -115,6 +138,8 @@ Output variables:
115138- ``ENABLE_SANITIZER_LEAK``: Leak sanitizer is supported
116139- ``ENABLE_SANITIZER_THREAD``: Thread sanitizer is supported
117140- ``ENABLE_SANITIZER_MEMORY``: Memory sanitizer is supported
141+ - ``ENABLE_SANITIZER_POINTER_COMPARE``: Pointer compare sanitizer is supported
142+ - ``ENABLE_SANITIZER_POINTER_SUBTRACT``: Pointer subtract sanitizer is supported
118143
119144
120145.. code:: cmake
@@ -123,7 +148,9 @@ Output variables:
123148 ENABLE_SANITIZER_UNDEFINED_BEHAVIOR
124149 ENABLE_SANITIZER_LEAK
125150 ENABLE_SANITIZER_THREAD
126- ENABLE_SANITIZER_MEMORY)
151+ ENABLE_SANITIZER_MEMORY
152+ ENABLE_SANITIZER_POINTER_COMPARE
153+ ENABLE_SANITIZER_POINTER_SUBTRACT)
127154
128155 # then pass the sanitizers (e.g. ${ENABLE_SANITIZER_ADDRESS}) to project_options(... ${ENABLE_SANITIZER_ADDRESS} ...)
129156
@@ -135,9 +162,13 @@ function(
135162 ENABLE_SANITIZER_LEAK
136163 ENABLE_SANITIZER_THREAD
137164 ENABLE_SANITIZER_MEMORY
165+ ENABLE_SANITIZER_POINTER_COMPARE
166+ ENABLE_SANITIZER_POINTER_SUBTRACT
138167)
139- set (SANITIZERS "" )
140- if (NOT "${CMAKE_SYSTEM_NAME} " STREQUAL "Windows" )
168+ set (SUPPORTED_SANITIZERS "" )
169+ if (NOT "${CMAKE_SYSTEM_NAME} " STREQUAL "Windows" AND (CMAKE_CXX_COMPILER_ID STREQUAL "GNU"
170+ OR CMAKE_CXX_COMPILER_ID MATCHES ".*Clang" )
171+ )
141172 set (HAS_SANITIZER_SUPPORT ON )
142173
143174 # Disable gcc sanitizer on some macos according to https://github.com/orgs/Homebrew/discussions/3384#discussioncomment-6264292
@@ -154,39 +185,58 @@ function(
154185 endif ()
155186
156187 if (HAS_SANITIZER_SUPPORT)
157- list (APPEND SANITIZERS "address" )
158- list (APPEND SANITIZERS "undefined" )
159- list (APPEND SANITIZERS "leak" )
160- list (APPEND SANITIZERS "thread" )
161- list (APPEND SANITIZERS "memory" )
188+ set (SUPPORTED_SANITIZERS "" )
189+ foreach (
190+ SANITIZER IN
191+ ITEMS "address"
192+ "undefined"
193+ "leak"
194+ "thread"
195+ "memory"
196+ "pointer-compare"
197+ "pointer-subtract"
198+ )
199+ if ((SANITIZER STREQUAL "pointer-compare" OR SANITIZER STREQUAL "pointer-subtract" )
200+ AND (NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_VERSION VERSION_LESS 8)
201+ )
202+ # pointer-compare and pointer-subtract are supported only by GCC 8 and later
203+ continue ()
204+ endif ()
205+
206+ list (APPEND SUPPORTED_SANITIZERS ${SANITIZER} )
207+ endforeach ()
162208 endif ()
163209 elseif (MSVC )
164210 # or it is MSVC and has run vcvarsall
165211 string (FIND "$ENV{PATH} " "$ENV{VSINSTALLDIR} " index_of_vs_install_dir)
166212 if (NOT "${index_of_vs_install_dir} " STREQUAL "-1" )
167- list (APPEND SANITIZERS "address" )
213+ list (APPEND SUPPORTED_SANITIZERS "address" )
168214 endif ()
169215 endif ()
170216
171- list (JOIN SANITIZERS "," LIST_OF_SANITIZERS)
217+ if (NOT SUPPORTED_SANITIZERS OR "${SUPPORTED_SANITIZERS} " STREQUAL "" )
218+ message (STATUS "No sanitizer is supported for the current platform/compiler" )
219+ return ()
220+ endif ()
172221
173- if (LIST_OF_SANITIZERS)
174- if (NOT "${LIST_OF_SANITIZERS} " STREQUAL "" )
175- if ("address" IN_LIST SANITIZERS)
176- set (${ENABLE_SANITIZER_ADDRESS} "ENABLE_SANITIZER_ADDRESS" PARENT_SCOPE)
177- endif ()
178- if ("undefined" IN_LIST SANITIZERS)
179- set (${ENABLE_SANITIZER_UNDEFINED_BEHAVIOR} "ENABLE_SANITIZER_UNDEFINED_BEHAVIOR" PARENT_SCOPE)
180- endif ()
181- if ("leak" IN_LIST SANITIZERS)
182- set (${ENABLE_SANITIZER_LEAK} "ENABLE_SANITIZER_LEAK" PARENT_SCOPE)
183- endif ()
184- if ("thread" IN_LIST SANITIZERS)
185- set (${ENABLE_SANITIZER_THREAD} "ENABLE_SANITIZER_THREAD" PARENT_SCOPE)
186- endif ()
187- if ("memory" IN_LIST SANITIZERS)
188- set (${ENABLE_SANITIZER_MEMORY} "ENABLE_SANITIZER_MEMORY" PARENT_SCOPE)
189- endif ()
222+ # Set the output variables
223+ foreach (
224+ SANITIZER IN
225+ ITEMS "address"
226+ "undefined"
227+ "leak"
228+ "thread"
229+ "memory"
230+ "pointer-compare"
231+ "pointer-subtract"
232+ )
233+ set (SANITIZER_UPPERCASE "${SANITIZER} " )
234+ string (TOUPPER ${SANITIZER} SANITIZER_UPPERCASE)
235+
236+ if (${SANITIZER} IN_LIST SUPPORTED_SANITIZERS)
237+ set (${ENABLE_SANITIZER_${SANITIZER_UPPERCASE} } "ENABLE_SANITIZER_${SANITIZER_UPPERCASE} " PARENT_SCOPE)
238+ else ()
239+ set (${ENABLE_SANITIZER_${SANITIZER_UPPERCASE} } "" PARENT_SCOPE)
190240 endif ()
191- endif ()
241+ endforeach ()
192242endfunction ()
0 commit comments