[clang] [llvm] [Clang] Add __builtin_allow_sanitize_check() (PR #172030)

Marco Elver via cfe-commits cfe-commits at lists.llvm.org
Wed Jan 7 15:06:55 PST 2026


melver wrote:

> > I guess maybe I'm not sure about the expected usage. Is the code you're trying to skip over code which performs checks, or code which instruments allocations? We could potentially have separate builtins which would return different values for those cases. The documentation doesn't clearly state which case the builtin is intended for.
> 
> It's intended for conditionally executing code that performs explicit checks using the runtime's internal state. For example like so:
> 
> ```
> if (__builtin_allow_sanitize_check("thread")) { // similar for asan, msan 
>    __tsan_read8(addr);  // explicit check
> }
> asm volatile (... inline asm that accesses addr ...);
> ```
> 
> In the Linux kernel more APIs exist to let the sanitizer runtime perform explicit checks given a pointer + size, but we need to skip over them if we're in a context where instrumentation (be implicit or explicit) is disallowed.
> 
> So it's really very similar to `__builtin_allow_runtime_check` in spirit, except the runtime here is a known sanitizer and the policy when we're allowed to check is much simpler for now.
> 
> If the documentation can be improved, we can certainly do so. Maybe instead of "Conditional Sanitizer Checks .." it should be "Explicit Sanitizer Checks .." with a more concrete example.

Perhaps something like this to clarify documentation:

```
diff --git a/clang/docs/AddressSanitizer.rst b/clang/docs/AddressSanitizer.rst
index dc0ba79f66cc..ca21b4c2f81a 100644
--- a/clang/docs/AddressSanitizer.rst
+++ b/clang/docs/AddressSanitizer.rst
@@ -254,37 +254,39 @@ AddressSanitizer also supports
 works similarly to ``__attribute__((no_sanitize("address")))``, but it also
 prevents instrumentation performed by other sanitizers.
 
-Conditional Sanitizer Checks with ``__builtin_allow_sanitize_check``
---------------------------------------------------------------------
+Explicit Sanitizer Checks with ``__builtin_allow_sanitize_check``
+-----------------------------------------------------------------
 
 The ``__builtin_allow_sanitize_check("address")`` builtin can be used to
-conditionally execute code only when AddressSanitizer is active for the current
-function (after inlining). This is particularly useful for inserting explicit,
-sanitizer-specific checks around operations like syscalls or inline assembly,
-which might otherwise be unchecked by the sanitizer.
+conditionally execute code only when AddressSanitizer checks are allowed for the
+current function (after inlining). This is particularly useful for inserting
+explicit, sanitizer-specific checks around operations like syscalls or inline
+assembly, which might otherwise be unchecked by the sanitizer.
 
 Example:
 
 .. code-block:: c
 
+    void __asan_load8(void *);
+
     inline __attribute__((always_inline))
-    void copy_to_device(void *addr, size_t size) {
-      if (__builtin_allow_sanitize_check("address")) {
-        // Custom checks that address range is valid.
-      }
-      // ... actual device memory copy logic, potentially a syscall ...
+    void my_helper(void *addr) {
+      if (__builtin_allow_sanitize_check("address"))
+        __asan_load8(addr);
+      // ... actual logic, e.g. inline assembly ...
+      asm volatile ("..." : : "r" (addr) : "memory");
     }
 
     void instrumented_function() {
       ...
-      copy_to_device(buf, sizeof(buf)); // checks are active
+      my_helper(buf); // checks are active
       ...
     }
 
     __attribute__((no_sanitize("address")))
     void uninstrumented_function() {
       ...
-      copy_to_device(buf, sizeof(buf)); // checks are skipped
+      my_helper(buf); // checks are skipped
       ...
     }
 
diff --git a/clang/docs/MemorySanitizer.rst b/clang/docs/MemorySanitizer.rst
index 54f88d076d36..75c112be1a09 100644
--- a/clang/docs/MemorySanitizer.rst
+++ b/clang/docs/MemorySanitizer.rst
@@ -101,37 +101,39 @@ positives and therefore should be used with care, and only if absolutely
 required; for example for certain code that cannot tolerate any instrumentation
 and resulting side-effects. This attribute overrides ``no_sanitize("memory")``.
 
-Conditional Sanitizer Checks with ``__builtin_allow_sanitize_check``
---------------------------------------------------------------------
+Explicit Sanitizer Checks with ``__builtin_allow_sanitize_check``
+-----------------------------------------------------------------
 
 The ``__builtin_allow_sanitize_check("memory")`` builtin can be used to
-conditionally execute code only when MemorySanitizer is active for the current
-function (after inlining). This is particularly useful for inserting explicit,
-sanitizer-specific checks around operations like syscalls or inline assembly,
-which might otherwise be unchecked by the sanitizer.
+conditionally execute code only when MemorySanitizer checks are allowed for the
+current function (after inlining). This is particularly useful for inserting
+explicit, sanitizer-specific checks around operations like syscalls or inline
+assembly, which might otherwise be unchecked by the sanitizer.
 
 Example:
 
 .. code-block:: c
 
+    void __msan_check_mem_is_initialized(const void *, size_t);
+
     inline __attribute__((always_inline))
-    void copy_to_device(void *addr, size_t size) {
-      if (__builtin_allow_sanitize_check("memory")) {
-        // Custom checks if `data` is initialized.
-      }
-      // ... actual device memory copy logic, potentially a syscall ...
+    void my_send(void *addr, size_t size) {
+      if (__builtin_allow_sanitize_check("memory"))
+        __msan_check_mem_is_initialized(addr, size);
+      // ... syscall or other logic where MSan may not see the access ...
+      send(addr, size);
     }
 
     void instrumented_function() {
       ...
-      copy_to_device(buf, sizeof(buf)); // checks are active
+      my_send(buf, sizeof(buf)); // checks are active
       ...
     }
 
     __attribute__((no_sanitize("memory")))
     void uninstrumented_function() {
       ...
-      copy_to_device(buf, sizeof(buf)); // checks are skipped
+      my_send(buf, sizeof(buf)); // checks are skipped
       ...
     }
 
diff --git a/clang/docs/ThreadSanitizer.rst b/clang/docs/ThreadSanitizer.rst
index c90d668600ec..81305b608a14 100644
--- a/clang/docs/ThreadSanitizer.rst
+++ b/clang/docs/ThreadSanitizer.rst
@@ -110,37 +110,39 @@ and only if absolutely required; for example for certain code that cannot
 tolerate any instrumentation and resulting side-effects. This attribute
 overrides ``no_sanitize("thread")``.
 
-Conditional Sanitizer Checks with ``__builtin_allow_sanitize_check``
---------------------------------------------------------------------
+Explicit Sanitizer Checks with ``__builtin_allow_sanitize_check``
+-----------------------------------------------------------------
 
 The ``__builtin_allow_sanitize_check("thread")`` builtin can be used to
-conditionally execute code only when ThreadSanitizer is active for the current
-function (after inlining). This is particularly useful for inserting explicit,
-sanitizer-specific checks around operations like syscalls or inline assembly,
-which might otherwise be unchecked by the sanitizer.
+conditionally execute code only when ThreadSanitizer checks are allowed for the
+current function (after inlining). This is particularly useful for inserting
+explicit, sanitizer-specific checks around operations like syscalls or inline
+assembly, which might otherwise be unchecked by the sanitizer.
 
 Example:
 
 .. code-block:: c
 
+    void __tsan_read8(void *);
+
     inline __attribute__((always_inline))
-    void copy_to_device(void *addr, size_t size) {
-      if (__builtin_allow_sanitize_check("thread")) {
-        // Custom checks that `data` is not concurrently modified.
-      }
-      // ... actual device memory copy logic, potentially a syscall ...
+    void my_helper(void *addr) {
+      if (__builtin_allow_sanitize_check("thread"))
+        __tsan_read8(addr);
+      // ... actual logic, e.g. inline assembly ...
+      asm volatile ("..." : : "r" (addr) : "memory");
     }
 
     void instrumented_function() {
       ...
-      copy_to_device(&shared_data, size); // checks are active
+      my_helper(&shared_data); // checks are active
       ...
     }
 
     __attribute__((no_sanitize("thread")))
     void uninstrumented_function() {
       ...
-      copy_to_device(&shared_data, size); // checks are skipped
+      my_helper(&shared_data); // checks are skipped
       ...
     }
 
```

https://github.com/llvm/llvm-project/pull/172030


More information about the cfe-commits mailing list