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