From f27cf74192b3c1d83329b2d4008e23595c59c5a1 Mon Sep 17 00:00:00 2001 From: Alexey Samsonov Date: Mon, 29 Sep 2025 22:21:02 -0700 Subject: [PATCH 1/4] [libc] Unify and extend no_sanitize attributes for strlen. Fast strlen implementations (naive wide-reads, SIMD-based, and x86_64/aarch64-optimized versions) all may perform technically-out-of-bound reads, which leads to reports under ASan, HWASan (on ARM machines), and also TSan (which also has the capability to detect heap out-of-bound reads). So, we need to explicitly disable instrumentation in all three cases. Tragically, Clang didn't support `[[gnu::no_sanitize]]` syntax until recently, and since we're supporting both GCC and Clang, we have to revert to `__attribute__` syntax. --- libc/src/string/memory_utils/aarch64/inline_strlen.h | 3 ++- libc/src/string/memory_utils/generic/inline_strlen.h | 3 ++- libc/src/string/memory_utils/x86_64/inline_strlen.h | 6 ++++-- libc/src/string/string_utils.h | 3 ++- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/libc/src/string/memory_utils/aarch64/inline_strlen.h b/libc/src/string/memory_utils/aarch64/inline_strlen.h index 36fd1aa636b54..9e5320afe987f 100644 --- a/libc/src/string/memory_utils/aarch64/inline_strlen.h +++ b/libc/src/string/memory_utils/aarch64/inline_strlen.h @@ -17,7 +17,8 @@ namespace LIBC_NAMESPACE_DECL { namespace neon { -[[gnu::no_sanitize_address]] [[maybe_unused]] LIBC_INLINE static size_t +__attribute__((no_sanitize("address", "hwaddress", "thread"))) +[[maybe_unused]] LIBC_INLINE static size_t string_length(const char *src) { using Vector __attribute__((may_alias)) = uint8x8_t; diff --git a/libc/src/string/memory_utils/generic/inline_strlen.h b/libc/src/string/memory_utils/generic/inline_strlen.h index d7435afb03719..0c13209d106d4 100644 --- a/libc/src/string/memory_utils/generic/inline_strlen.h +++ b/libc/src/string/memory_utils/generic/inline_strlen.h @@ -24,7 +24,8 @@ LIBC_INLINE constexpr cpp::simd_mask shift_mask(cpp::simd_mask m, return cpp::bit_cast>(r); } -[[clang::no_sanitize("address")]] LIBC_INLINE size_t +__attribute__((no_sanitize("address", "hwaddress", "thread"))) +LIBC_INLINE size_t string_length(const char *src) { constexpr cpp::simd null_byte = cpp::splat('\0'); diff --git a/libc/src/string/memory_utils/x86_64/inline_strlen.h b/libc/src/string/memory_utils/x86_64/inline_strlen.h index 739f8c1aaddbc..047f10d8b2bad 100644 --- a/libc/src/string/memory_utils/x86_64/inline_strlen.h +++ b/libc/src/string/memory_utils/x86_64/inline_strlen.h @@ -18,12 +18,14 @@ namespace LIBC_NAMESPACE_DECL { namespace string_length_internal { // Return a bit-mask with the nth bit set if the nth-byte in block_ptr is zero. template -[[gnu::no_sanitize_address]] LIBC_INLINE static Mask +__attribute__((no_sanitize("address", "hwaddress", "thread"))) +LIBC_INLINE static Mask compare_and_mask(const Vector *block_ptr); template )> -[[gnu::no_sanitize_address]] LIBC_INLINE static size_t +__attribute__((no_sanitize("address", "hwaddress", "thread"))) +LIBC_INLINE static size_t string_length_vector(const char *src) { uintptr_t misalign_bytes = reinterpret_cast(src) % sizeof(Vector); diff --git a/libc/src/string/string_utils.h b/libc/src/string/string_utils.h index 9d636d02f4756..6ee94c244034b 100644 --- a/libc/src/string/string_utils.h +++ b/libc/src/string/string_utils.h @@ -119,7 +119,8 @@ template LIBC_INLINE size_t string_length(const T *src) { } template -[[gnu::no_sanitize_address]] LIBC_INLINE void * +__attribute__((no_sanitize("address", "hwaddress", "thread"))) +LIBC_INLINE void * find_first_character_wide_read(const unsigned char *src, unsigned char ch, size_t n) { const unsigned char *char_ptr = src; From f2e0c24f01f93569e00123850ceb8a514228586c Mon Sep 17 00:00:00 2001 From: Alexey Samsonov Date: Mon, 29 Sep 2025 22:41:22 -0700 Subject: [PATCH 2/4] move attribute list to a different place --- libc/src/string/memory_utils/aarch64/inline_strlen.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libc/src/string/memory_utils/aarch64/inline_strlen.h b/libc/src/string/memory_utils/aarch64/inline_strlen.h index 9e5320afe987f..49b901f2014a2 100644 --- a/libc/src/string/memory_utils/aarch64/inline_strlen.h +++ b/libc/src/string/memory_utils/aarch64/inline_strlen.h @@ -17,8 +17,8 @@ namespace LIBC_NAMESPACE_DECL { namespace neon { -__attribute__((no_sanitize("address", "hwaddress", "thread"))) -[[maybe_unused]] LIBC_INLINE static size_t +[[maybe_unused]] __attribute__((no_sanitize("address", "hwaddress", "thread"))) +LIBC_INLINE static size_t string_length(const char *src) { using Vector __attribute__((may_alias)) = uint8x8_t; From 54adc87a3f8373a3ee7def359092708110a8fade Mon Sep 17 00:00:00 2001 From: Alexey Samsonov Date: Tue, 30 Sep 2025 10:26:14 -0700 Subject: [PATCH 3/4] Move no_sanitize attribute to macro --- libc/src/__support/macros/attributes.h | 10 ++++++++++ libc/src/string/CMakeLists.txt | 1 + libc/src/string/memory_utils/aarch64/inline_strlen.h | 3 +-- libc/src/string/memory_utils/generic/inline_strlen.h | 4 +--- libc/src/string/memory_utils/x86_64/inline_strlen.h | 6 ++---- libc/src/string/string_utils.h | 4 ++-- utils/bazel/llvm-project-overlay/libc/BUILD.bazel | 1 + 7 files changed, 18 insertions(+), 11 deletions(-) diff --git a/libc/src/__support/macros/attributes.h b/libc/src/__support/macros/attributes.h index 145aa3b65057c..7dd28fca97db7 100644 --- a/libc/src/__support/macros/attributes.h +++ b/libc/src/__support/macros/attributes.h @@ -81,4 +81,14 @@ LIBC_THREAD_MODE_EXTERNAL. #define LIBC_HAS_VECTOR_TYPE 0 #endif +#if __has_attribute(no_sanitize) +// Disable regular and hardware-supported ASan for functions that may +// intentionally make out-of-bounds access. Disable TSan as well, as it detects +// out-of-bounds accesses to heap memory. +#define LIBC_NOSANITIZE_OOB_ACCESS \ + __attribute__((no_sanitize("address", "hwaddress", "thread"))) +#else +#define LIBC_NOSANITIZE_OOB_ACCESS +#endif + #endif // LLVM_LIBC_SRC___SUPPORT_MACROS_ATTRIBUTES_H diff --git a/libc/src/string/CMakeLists.txt b/libc/src/string/CMakeLists.txt index b8cdb2a7d3538..83c956429be24 100644 --- a/libc/src/string/CMakeLists.txt +++ b/libc/src/string/CMakeLists.txt @@ -22,6 +22,7 @@ add_header_library( libc.src.__support.CPP.type_traits libc.src.__support.CPP.simd libc.src.__support.common + libc.src.__support.macros.attributes libc.src.string.memory_utils.inline_memcpy ${string_config_options} ) diff --git a/libc/src/string/memory_utils/aarch64/inline_strlen.h b/libc/src/string/memory_utils/aarch64/inline_strlen.h index 49b901f2014a2..fa6b7e36eadfe 100644 --- a/libc/src/string/memory_utils/aarch64/inline_strlen.h +++ b/libc/src/string/memory_utils/aarch64/inline_strlen.h @@ -17,8 +17,7 @@ namespace LIBC_NAMESPACE_DECL { namespace neon { -[[maybe_unused]] __attribute__((no_sanitize("address", "hwaddress", "thread"))) -LIBC_INLINE static size_t +[[maybe_unused]] LIBC_NOSANITIZE_OOB_ACCESS LIBC_INLINE static size_t string_length(const char *src) { using Vector __attribute__((may_alias)) = uint8x8_t; diff --git a/libc/src/string/memory_utils/generic/inline_strlen.h b/libc/src/string/memory_utils/generic/inline_strlen.h index 0c13209d106d4..f14c81675611c 100644 --- a/libc/src/string/memory_utils/generic/inline_strlen.h +++ b/libc/src/string/memory_utils/generic/inline_strlen.h @@ -24,9 +24,7 @@ LIBC_INLINE constexpr cpp::simd_mask shift_mask(cpp::simd_mask m, return cpp::bit_cast>(r); } -__attribute__((no_sanitize("address", "hwaddress", "thread"))) -LIBC_INLINE size_t -string_length(const char *src) { +LIBC_NOSANITIZE_OOB_ACCESS LIBC_INLINE size_t string_length(const char *src) { constexpr cpp::simd null_byte = cpp::splat('\0'); size_t alignment = alignof(cpp::simd); diff --git a/libc/src/string/memory_utils/x86_64/inline_strlen.h b/libc/src/string/memory_utils/x86_64/inline_strlen.h index 047f10d8b2bad..c388724cf7fe0 100644 --- a/libc/src/string/memory_utils/x86_64/inline_strlen.h +++ b/libc/src/string/memory_utils/x86_64/inline_strlen.h @@ -18,14 +18,12 @@ namespace LIBC_NAMESPACE_DECL { namespace string_length_internal { // Return a bit-mask with the nth bit set if the nth-byte in block_ptr is zero. template -__attribute__((no_sanitize("address", "hwaddress", "thread"))) -LIBC_INLINE static Mask +LIBC_NOSANITIZE_OOB_ACCESS LIBC_INLINE static Mask compare_and_mask(const Vector *block_ptr); template )> -__attribute__((no_sanitize("address", "hwaddress", "thread"))) -LIBC_INLINE static size_t +LIBC_NOSANITIZE_OOB_ACCESS LIBC_INLINE static size_t string_length_vector(const char *src) { uintptr_t misalign_bytes = reinterpret_cast(src) % sizeof(Vector); diff --git a/libc/src/string/string_utils.h b/libc/src/string/string_utils.h index 6ee94c244034b..5cb96533d0398 100644 --- a/libc/src/string/string_utils.h +++ b/libc/src/string/string_utils.h @@ -19,6 +19,7 @@ #include "hdr/types/size_t.h" #include "src/__support/CPP/bitset.h" #include "src/__support/CPP/type_traits.h" // cpp::is_same_v +#include "src/__support/macros/attributes.h" #include "src/__support/macros/config.h" #include "src/__support/macros/optimization.h" // LIBC_UNLIKELY #include "src/string/memory_utils/inline_memcpy.h" @@ -119,8 +120,7 @@ template LIBC_INLINE size_t string_length(const T *src) { } template -__attribute__((no_sanitize("address", "hwaddress", "thread"))) -LIBC_INLINE void * +LIBC_NOSANITIZE_OOB_ACCESS LIBC_INLINE void * find_first_character_wide_read(const unsigned char *src, unsigned char ch, size_t n) { const unsigned char *char_ptr = src; diff --git a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel index 8d9e80393bf20..287750c30a2ac 100644 --- a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel +++ b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel @@ -5320,6 +5320,7 @@ libc_support_library( ":__support_common", ":__support_cpp_bitset", ":__support_cpp_type_traits", + ":__support_macros_attributes", ":__support_macros_optimization", ":hdr_limits_macros", ":llvm_libc_types_size_t", From 1a2f74fb1c7e2d79e588a1110b7273c9216aa733 Mon Sep 17 00:00:00 2001 From: Alexey Samsonov Date: Tue, 30 Sep 2025 11:14:19 -0700 Subject: [PATCH 4/4] rename the macro to match attribute name. --- libc/src/__support/macros/attributes.h | 4 ++-- libc/src/string/memory_utils/aarch64/inline_strlen.h | 2 +- libc/src/string/memory_utils/generic/inline_strlen.h | 2 +- libc/src/string/memory_utils/x86_64/inline_strlen.h | 4 ++-- libc/src/string/string_utils.h | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/libc/src/__support/macros/attributes.h b/libc/src/__support/macros/attributes.h index 7dd28fca97db7..d5ff028634940 100644 --- a/libc/src/__support/macros/attributes.h +++ b/libc/src/__support/macros/attributes.h @@ -85,10 +85,10 @@ LIBC_THREAD_MODE_EXTERNAL. // Disable regular and hardware-supported ASan for functions that may // intentionally make out-of-bounds access. Disable TSan as well, as it detects // out-of-bounds accesses to heap memory. -#define LIBC_NOSANITIZE_OOB_ACCESS \ +#define LIBC_NO_SANITIZE_OOB_ACCESS \ __attribute__((no_sanitize("address", "hwaddress", "thread"))) #else -#define LIBC_NOSANITIZE_OOB_ACCESS +#define LIBC_NO_SANITIZE_OOB_ACCESS #endif #endif // LLVM_LIBC_SRC___SUPPORT_MACROS_ATTRIBUTES_H diff --git a/libc/src/string/memory_utils/aarch64/inline_strlen.h b/libc/src/string/memory_utils/aarch64/inline_strlen.h index fa6b7e36eadfe..87f5ccdd56e23 100644 --- a/libc/src/string/memory_utils/aarch64/inline_strlen.h +++ b/libc/src/string/memory_utils/aarch64/inline_strlen.h @@ -17,7 +17,7 @@ namespace LIBC_NAMESPACE_DECL { namespace neon { -[[maybe_unused]] LIBC_NOSANITIZE_OOB_ACCESS LIBC_INLINE static size_t +[[maybe_unused]] LIBC_NO_SANITIZE_OOB_ACCESS LIBC_INLINE static size_t string_length(const char *src) { using Vector __attribute__((may_alias)) = uint8x8_t; diff --git a/libc/src/string/memory_utils/generic/inline_strlen.h b/libc/src/string/memory_utils/generic/inline_strlen.h index f14c81675611c..69700e801bcea 100644 --- a/libc/src/string/memory_utils/generic/inline_strlen.h +++ b/libc/src/string/memory_utils/generic/inline_strlen.h @@ -24,7 +24,7 @@ LIBC_INLINE constexpr cpp::simd_mask shift_mask(cpp::simd_mask m, return cpp::bit_cast>(r); } -LIBC_NOSANITIZE_OOB_ACCESS LIBC_INLINE size_t string_length(const char *src) { +LIBC_NO_SANITIZE_OOB_ACCESS LIBC_INLINE size_t string_length(const char *src) { constexpr cpp::simd null_byte = cpp::splat('\0'); size_t alignment = alignof(cpp::simd); diff --git a/libc/src/string/memory_utils/x86_64/inline_strlen.h b/libc/src/string/memory_utils/x86_64/inline_strlen.h index c388724cf7fe0..9e10d58363393 100644 --- a/libc/src/string/memory_utils/x86_64/inline_strlen.h +++ b/libc/src/string/memory_utils/x86_64/inline_strlen.h @@ -18,12 +18,12 @@ namespace LIBC_NAMESPACE_DECL { namespace string_length_internal { // Return a bit-mask with the nth bit set if the nth-byte in block_ptr is zero. template -LIBC_NOSANITIZE_OOB_ACCESS LIBC_INLINE static Mask +LIBC_NO_SANITIZE_OOB_ACCESS LIBC_INLINE static Mask compare_and_mask(const Vector *block_ptr); template )> -LIBC_NOSANITIZE_OOB_ACCESS LIBC_INLINE static size_t +LIBC_NO_SANITIZE_OOB_ACCESS LIBC_INLINE static size_t string_length_vector(const char *src) { uintptr_t misalign_bytes = reinterpret_cast(src) % sizeof(Vector); diff --git a/libc/src/string/string_utils.h b/libc/src/string/string_utils.h index 5cb96533d0398..7feef56fb3676 100644 --- a/libc/src/string/string_utils.h +++ b/libc/src/string/string_utils.h @@ -120,7 +120,7 @@ template LIBC_INLINE size_t string_length(const T *src) { } template -LIBC_NOSANITIZE_OOB_ACCESS LIBC_INLINE void * +LIBC_NO_SANITIZE_OOB_ACCESS LIBC_INLINE void * find_first_character_wide_read(const unsigned char *src, unsigned char ch, size_t n) { const unsigned char *char_ptr = src;