Paul Eggert wrote:
> these functions can 
> simply avoid the optimized-but-in-theory-invalid behavior if address 
> sanitization is in effect. Although that slows things down, sanitization 
> is already slower so it's not that big a deal. I installed the attached 
> to do that.

The patch, unfortunately, has no effect with clang versions < 22.

Recall that the macro __SANITIZE_ADDRESS__ is defined by -fsanitize=address
only
  - by gcc (version 4.8 or newer),
  - by clang version 22 or newer.
Clang versions 4 to 21 also support -fsanitize=address but don't define
__SANITIZE_ADDRESS__.

How to reproduce:
 1. Create a testdir for modules
      memchr memchr2 rawmemchr strchrnul
 2. Build it with clang 21, with CC option -fsanitize=address and
    ASAN_OPTIONS="detect_leaks=0 abort_on_error=1 allocator_may_return_null=1",
    configuring with
      ac_cv_func_rawmemchr=no gl_cv_func_memchr_works=no \
      ac_cv_func_strchrnul=no gl_cv_func_strchrnul_works=no \
      ./configure
 3. $ make check
    ...
    FAIL: test-rawmemchr
    ...

This patch fixes it.


2026-07-26  Bruno Haible  <[email protected]>

        Pacify -fsanitize=address in rawmemchr, strchrnul also with clang < 22.
        * lib/memchr.c (__has_feature): New macro.
        (__memchr): Test the feature 'address_sanitizer'.
        * lib/memchr2.c (__has_feature): New macro.
        (memchr2): Test the feature 'address_sanitizer'.
        * lib/rawmemchr.c (__has_feature): New macro.
        (rawmemchr): Test the feature 'address_sanitizer'.
        * lib/strchrnul.c (__has_feature): New macro.
        (strchrnul): Test the feature 'address_sanitizer'.

diff --git a/lib/memchr.c b/lib/memchr.c
index b023f478bf..fb1a81ab8e 100644
--- a/lib/memchr.c
+++ b/lib/memchr.c
@@ -54,6 +54,10 @@
 # define __memchr memchr
 #endif
 
+#ifndef __has_feature
+# define __has_feature(a) 0
+#endif
+
 /* Search no more than N bytes of S for C.  */
 void *
 __memchr (void const *s, int c_in, size_t n)
@@ -66,7 +70,8 @@ __memchr (void const *s, int c_in, size_t n)
      so suppress this optimization on platforms where it is known to be
      dangerous, namely, those using address sanitization.  */
 
-#if ! (defined __SANITIZE_ADDRESS__ || defined __CHERI_PURE_CAPABILITY__)
+#if ! (defined __SANITIZE_ADDRESS__ || __has_feature (address_sanitizer) \
+       || defined __CHERI_PURE_CAPABILITY__)
 
   /* On 32-bit hardware, choosing longword to be a 32-bit unsigned
      long instead of a 64-bit uintmax_t tends to give better
diff --git a/lib/memchr2.c b/lib/memchr2.c
index ff2bd9c0ad..5f9ffd9640 100644
--- a/lib/memchr2.c
+++ b/lib/memchr2.c
@@ -29,6 +29,10 @@
 #include <stdint.h>
 #include <string.h>
 
+#ifndef __has_feature
+# define __has_feature(a) 0
+#endif
+
 /* Return the first address of either C1 or C2 (treated as unsigned
    char) that occurs within N bytes of the memory region S.  If
    neither byte appears, return NULL.  */
@@ -44,7 +48,8 @@ memchr2 (void const *s, int c1_in, int c2_in, size_t n)
      so suppress this optimization on platforms where it is known to be
      dangerous, namely, those using address sanitization.  */
 
-#if ! (defined __SANITIZE_ADDRESS__ || defined __CHERI_PURE_CAPABILITY__)
+#if ! (defined __SANITIZE_ADDRESS__ || __has_feature (address_sanitizer) \
+       || defined __CHERI_PURE_CAPABILITY__)
 
   /* On 32-bit hardware, choosing longword to be a 32-bit unsigned
      long instead of a 64-bit uintmax_t tends to give better
diff --git a/lib/rawmemchr.c b/lib/rawmemchr.c
index e3cb63bd21..764d63339e 100644
--- a/lib/rawmemchr.c
+++ b/lib/rawmemchr.c
@@ -25,6 +25,10 @@
 # include <limits.h>
 # include <stdint.h>
 
+# ifndef __has_feature
+#  define __has_feature(a) 0
+# endif
+
 
 /* Find the first occurrence of C in S.  */
 void *
@@ -49,7 +53,7 @@ rawmemchr (const void *s, int c_in)
      so suppress this optimization on platforms where it is known to be
      dangerous, namely, those using address sanitization.  */
 
-#  ifndef __SANITIZE_ADDRESS__
+#  if ! (defined __SANITIZE_ADDRESS__ || __has_feature (address_sanitizer))
 
   /* You can change this typedef to experiment with performance.  */
   typedef uintptr_t longword _GL_ATTRIBUTE_MAY_ALIAS;
diff --git a/lib/strchrnul.c b/lib/strchrnul.c
index 74dd7e2aaf..b17afc1833 100644
--- a/lib/strchrnul.c
+++ b/lib/strchrnul.c
@@ -19,6 +19,10 @@
 /* Specification.  */
 #include <string.h>
 
+#ifndef __has_feature
+# define __has_feature(a) 0
+#endif
+
 /* Find the first occurrence of C in S or the final NUL byte.  */
 char *
 strchrnul (const char *s, int c_in)
@@ -33,7 +37,8 @@ strchrnul (const char *s, int c_in)
      so suppress this optimization on platforms where it is known to be
      dangerous, namely, those using address sanitization.  */
 
-#if ! (defined __SANITIZE_ADDRESS__ || defined __CHERI_PURE_CAPABILITY__)
+#if ! (defined __SANITIZE_ADDRESS__ || __has_feature (address_sanitizer) \
+       || defined __CHERI_PURE_CAPABILITY__)
 
   /* On 32-bit hardware, choosing longword to be a 32-bit unsigned
      long instead of a 64-bit uintmax_t tends to give better




Reply via email to