git: d08296c7ab0d - main - libc: Implement qualifier-preserving standard library functions
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Thu, 13 Aug 2026 16:39:29 UTC
The branch main has been updated by kfv:
URL: https://cgit.FreeBSD.org/src/commit/?id=d08296c7ab0d7bb259bf7b8cdf9ffb819c1929ab
commit d08296c7ab0d7bb259bf7b8cdf9ffb819c1929ab
Author: Faraz Vahedi <kfv@FreeBSD.org>
AuthorDate: 2026-06-19 21:50:20 +0000
Commit: Faraz Vahedi <kfv@FreeBSD.org>
CommitDate: 2026-08-13 16:38:49 +0000
libc: Implement qualifier-preserving standard library functions
Several standard library functions are specified to return an unqualified
pointer while accepting a pointer to a potentially const-qualified object.
N3020 addresses this behaviour, discarding qualifiers due to incompatible
pointer types, by introducing qualifier-preserving macros for the affected
set of standard library functions.
Add `__qualsel()` helper to `<sys/cdefs.h>`, implemented using the generic
selection, and define qualifier-preserving macros for that set of functions
in `<string.h>`, `<wchar.h>`, and `<stdlib.h>`.
Macros are gated on `_STDC_VERSION__ >= 202311L && !__cplusplus`, therefore
there is no behavioural change for earlier C modes or C++ translation units.
The kernel is likewise unaffected, as it does not include userland headers.
As function-like macros, they are transparent except at a call site where
the address-of operator is applied, the macro is suppressed via `#undef`,
or the identifier appears in parenthesised form; all of which cause the
underlying function designator to be used instead.
Reviewed by: fuz
Approved by: fuz (mentor)
MFC after: 1 month
Pull Request: https://github.com/freebsd/freebsd-src/pull/2288
---
include/stdlib.h | 12 ++++++++++
include/string.h | 28 ++++++++++++++++++++++++
include/wchar.h | 14 ++++++++++++
lib/libc/stdlib/bsearch.3 | 5 +++--
lib/libc/string/bstring.3 | 9 ++++++--
lib/libc/string/memchr.3 | 13 +++++++----
lib/libc/string/memmem.3 | 5 +++++
lib/libc/string/strchr.3 | 12 ++++++++--
lib/libc/string/string.3 | 16 +++++++++-----
lib/libc/string/strpbrk.3 | 8 +++----
lib/libc/string/strstr.3 | 21 ++++++++++++++----
lib/libc/string/wmemchr.3 | 56 ++++++++++++++++++++++++++++++++++++++---------
sys/sys/cdefs.h | 21 ++++++++++++++++++
13 files changed, 187 insertions(+), 33 deletions(-)
diff --git a/include/stdlib.h b/include/stdlib.h
index c1f465e21fd1..d61e2de99693 100644
--- a/include/stdlib.h
+++ b/include/stdlib.h
@@ -418,4 +418,16 @@ errno_t qsort_s(void *, rsize_t, rsize_t,
__END_DECLS
__NULLABILITY_PRAGMA_POP
+#if defined(__qualsel) && !defined(__cplusplus) && \
+ defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L
+#define bsearch(key, base, nmemb, size, compar) __qualsel((base), \
+ (const void *)(bsearch)((key), (base), (nmemb), (size), (compar)), \
+ (bsearch)((key), (base), (nmemb), (size), (compar)))
+#ifdef __BLOCKS__
+#define bsearch_b(key, base, nmemb, size, compar) __qualsel((base), \
+ (const void *)(bsearch_b)((key), (base), (nmemb), (size), (compar)), \
+ (bsearch_b)((key), (base), (nmemb), (size), (compar)))
+#endif
+#endif
+
#endif /* !_STDLIB_H_ */
diff --git a/include/string.h b/include/string.h
index e9aa1b55281a..98a0a6396848 100644
--- a/include/string.h
+++ b/include/string.h
@@ -205,4 +205,32 @@ errno_t memset_s(void *, rsize_t, int, rsize_t);
#endif /* __EXT1_VISIBLE */
__END_DECLS
+#if defined(__qualsel) && !defined(__cplusplus) && \
+ defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L
+#define memchr(b, c, n) __qualsel((b), \
+ (const void *)(memchr)((b), (c), (n)), (memchr)((b), (c), (n)))
+#define strchr(s, c) __qualsel((s), \
+ (const char *)(strchr)((s), (c)), (strchr)((s), (c)))
+#define strpbrk(s, charset) __qualsel((s), \
+ (const char *)(strpbrk)((s), (charset)), (strpbrk)((s), (charset)))
+#define strrchr(s, c) __qualsel((s), \
+ (const char *)(strrchr)((s), (c)), (strrchr)((s), (c)))
+#define strstr(s, find) __qualsel((s), \
+ (const char *)(strstr)((s), (find)), (strstr)((s), (find)))
+#if __BSD_VISIBLE
+#define memmem(b, blen, pat, plen) __qualsel((b), \
+ (const void *)(memmem)((b), (blen), (pat), (plen)), \
+ (memmem)((b), (blen), (pat), (plen)))
+#define memrchr(b, c, n) __qualsel((b), \
+ (const void *)(memrchr)((b), (c), (n)), (memrchr)((b), (c), (n)))
+#define strcasestr(s, find) __qualsel((s), \
+ (const char *)(strcasestr)((s), (find)), (strcasestr)((s), (find)))
+#define strchrnul(s, c) __qualsel((s), \
+ (const char *)(strchrnul)((s), (c)), (strchrnul)((s), (c)))
+#define strnstr(s, find, slen) __qualsel((s), \
+ (const char *)(strnstr)((s), (find), (slen)), \
+ (strnstr)((s), (find), (slen)))
+#endif /* __BSD_VISIBLE */
+#endif
+
#endif /* _STRING_H_ */
diff --git a/include/wchar.h b/include/wchar.h
index bf07062935d5..1447d083a889 100644
--- a/include/wchar.h
+++ b/include/wchar.h
@@ -246,4 +246,18 @@ size_t wcslcpy(wchar_t *, const wchar_t *, size_t);
#endif
__END_DECLS
+#if defined(__qualsel) && !defined(__cplusplus) && \
+ defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L
+#define wcschr(s, c) __qualsel((s), \
+ (const wchar_t *)(wcschr)((s), (c)), (wcschr)((s), (c)))
+#define wcspbrk(s, set) __qualsel((s), \
+ (const wchar_t *)(wcspbrk)((s), (set)), (wcspbrk)((s), (set)))
+#define wcsrchr(s, c) __qualsel((s), \
+ (const wchar_t *)(wcsrchr)((s), (c)), (wcsrchr)((s), (c)))
+#define wcsstr(s, find) __qualsel((s), \
+ (const wchar_t *)(wcsstr)((s), (find)), (wcsstr)((s), (find)))
+#define wmemchr(s, c, n) __qualsel((s), \
+ (const wchar_t *)(wmemchr)((s), (c), (n)), (wmemchr)((s), (c), (n)))
+#endif
+
#endif /* !_WCHAR_H_ */
diff --git a/lib/libc/stdlib/bsearch.3 b/lib/libc/stdlib/bsearch.3
index 712be0f98381..68c0cae5154a 100644
--- a/lib/libc/stdlib/bsearch.3
+++ b/lib/libc/stdlib/bsearch.3
@@ -29,7 +29,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd July 17, 2019
+.Dd June 21, 2026
.Dt BSEARCH 3
.Os
.Sh NAME
@@ -153,4 +153,5 @@ main(void)
The
.Fn bsearch
function conforms to
-.St -isoC .
+.St -isoC-2023 ,
+where it is specified as a qualifier-preserving function.
diff --git a/lib/libc/string/bstring.3 b/lib/libc/string/bstring.3
index dd89af44fc4a..25168404029b 100644
--- a/lib/libc/string/bstring.3
+++ b/lib/libc/string/bstring.3
@@ -27,7 +27,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd December 5, 2023
+.Dd June 21, 2026
.Dt BSTRING 3
.Os
.Sh NAME
@@ -88,7 +88,6 @@ See the specific manual pages for more information.
.Xr memset 3
.Sh STANDARDS
The functions
-.Fn memchr ,
.Fn memcmp ,
.Fn memcpy ,
.Fn memmove ,
@@ -96,6 +95,12 @@ and
.Fn memset
conform to
.St -isoC .
+.Pp
+The
+.Fn memchr
+function conforms to
+.St -isoC-2023 ,
+where it is specified as a qualifier-preserving function.
.Sh HISTORY
The functions
.Fn bzero
diff --git a/lib/libc/string/memchr.3 b/lib/libc/string/memchr.3
index c50e932d3382..da96256e976d 100644
--- a/lib/libc/string/memchr.3
+++ b/lib/libc/string/memchr.3
@@ -29,7 +29,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd April 9, 2008
+.Dd June 21, 2026
.Dt MEMCHR 3
.Os
.Sh NAME
@@ -92,13 +92,18 @@ bytes.
.Sh STANDARDS
The
.Fn memchr
-function
-conforms to
-.St -isoC .
+function conforms to
+.St -isoC-2023 ,
+where it is specified as a qualifier-preserving function.
.Pp
The
.Fn memrchr
function is a GNU extension and conforms to no standard.
+Like the qualifier-preserving functions in
+.St -isoC-2023 ,
+.Fx implements
+.Fn memrchr
+as qualifier-preserving as well.
.Sh HISTORY
The
.Fn memrchr
diff --git a/lib/libc/string/memmem.3 b/lib/libc/string/memmem.3
index 8e8aa6dafa99..ca236839e755 100644
--- a/lib/libc/string/memmem.3
+++ b/lib/libc/string/memmem.3
@@ -71,6 +71,11 @@ is returned.
.Fn memmem
conforms to
.St -p1003.1-2024 .
+Like the qualifier-preserving functions in
+.St -isoC-2023 ,
+.Fx implements
+.Fn memmem
+as qualifier-preserving as well.
.Sh HISTORY
The
.Fn memmem
diff --git a/lib/libc/string/strchr.3 b/lib/libc/string/strchr.3
index 45179a0001fc..a2f5b32c32c9 100644
--- a/lib/libc/string/strchr.3
+++ b/lib/libc/string/strchr.3
@@ -29,7 +29,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd February 13, 2013
+.Dd June 21, 2026
.Dt STRCHR 3
.Os
.Sh NAME
@@ -109,12 +109,20 @@ The functions
and
.Fn strrchr
conform to
-.St -isoC .
+.St -isoC-2023 ,
+where they are specified as qualifier-preserving functions.
+.Pp
The function
.Fn strchrnul
is a
.Tn GNU
extension.
+Like the qualifier-preserving functions in
+.St -isoC-2023 ,
+.Fx
+implements
+.Fn strchrnul
+as qualifier-preserving as well.
.Sh HISTORY
The
.Fn strchrnul
diff --git a/lib/libc/string/string.3 b/lib/libc/string/string.3
index 3ffea6ce0369..c91f6317b5db 100644
--- a/lib/libc/string/string.3
+++ b/lib/libc/string/string.3
@@ -27,7 +27,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd September 2, 2023
+.Dd June 21, 2026
.Dt STRING 3
.Os
.Sh NAME
@@ -136,20 +136,26 @@ for size limitations.
The
.Fn strcat ,
.Fn strncat ,
-.Fn strchr ,
-.Fn strrchr ,
.Fn strcmp ,
.Fn strncmp ,
.Fn strcpy ,
.Fn strncpy ,
.Fn strerror ,
.Fn strlen ,
-.Fn strpbrk ,
.Fn strspn ,
.Fn strcspn ,
-.Fn strstr ,
and
.Fn strtok
functions
conform to
.St -isoC .
+.Pp
+The
+.Fn strchr ,
+.Fn strrchr ,
+.Fn strpbrk ,
+and
+.Fn strstr
+functions conform to
+.St -isoC-2023 ,
+where they are specified as qualifier-preserving functions.
diff --git a/lib/libc/string/strpbrk.3 b/lib/libc/string/strpbrk.3
index cdccf0da957f..fd63aacfa965 100644
--- a/lib/libc/string/strpbrk.3
+++ b/lib/libc/string/strpbrk.3
@@ -29,7 +29,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd June 4, 1993
+.Dd June 21, 2026
.Dt STRPBRK 3
.Os
.Sh NAME
@@ -69,6 +69,6 @@ returns NULL.
.Sh STANDARDS
The
.Fn strpbrk
-function
-conforms to
-.St -isoC .
+function conforms to
+.St -isoC-2023 ,
+where it is specified as a qualifier-preserving function.
diff --git a/lib/libc/string/strstr.3 b/lib/libc/string/strstr.3
index 8957388db535..e04e882eec6a 100644
--- a/lib/libc/string/strstr.3
+++ b/lib/libc/string/strstr.3
@@ -30,7 +30,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd October 11, 2001
+.Dd June 21, 2026
.Dt STRSTR 3
.Os
.Sh NAME
@@ -147,9 +147,22 @@ ptr = strnstr(largestring, smallstring, 4);
.Sh STANDARDS
The
.Fn strstr
-function
-conforms to
-.St -isoC .
+function conforms to
+.St -isoC-2023 ,
+where it is specified as a qualifier-preserving function.
+.Pp
+The
+.Fn strcasestr
+function is a
+.Tn GNU
+extension, and the
+.Fn strnstr
+function is a
+.Bx
+extension.
+Like the qualifier-preserving functions in
+.St -isoC-2023 ,
+.Fx implements them as qualifier-preserving as well.
.Sh HISTORY
The
.Fn strnstr
diff --git a/lib/libc/string/wmemchr.3 b/lib/libc/string/wmemchr.3
index c1701facb7d5..fddb59a1f49f 100644
--- a/lib/libc/string/wmemchr.3
+++ b/lib/libc/string/wmemchr.3
@@ -154,21 +154,57 @@ counterpart, such as
.Xr strspn 3 ,
.Xr strstr 3
.Sh STANDARDS
-These functions conform to
-.St -isoC-99 ,
-with the exception of
+Functions
+.Fn wmemcmp ,
+.Fn wmemcpy ,
+.Fn wmemmove ,
+.Fn wmemset ,
+.Fn wcscat ,
+.Fn wcscmp ,
+.Fn wcscpy ,
+.Fn wcscspn ,
+.Fn wcslen ,
+.Fn wcsncat ,
+.Fn wcsncmp ,
+.Fn wcsncpy ,
+and
+.Fn wcsspn
+conform to
+.St -isoC-99 .
+.Pp
+Functions
+.Fn wmemchr ,
+.Fn wcschr ,
+.Fn wcspbrk ,
+.Fn wcsrchr ,
+and
+.Fn wcsstr
+conform to
+.St -isoC-2023 ,
+where they are specified as qualifier-preserving functions.
+.Pp
+Functions
.Fn wcpcpy ,
.Fn wcpncpy ,
.Fn wcscasecmp ,
.Fn wcsdup ,
.Fn wcsncasecmp ,
and
-.Fn wcsnlen ,
-which conform to
-.St -p1003.1-2008 ;
+.Fn wcsnlen
+conform to
+.St -p1003.1-2008 ,
+and functions
+.Fn wcslcat
and
-.Fn wcslcat ,
.Fn wcslcpy ,
-and
-.Fn wmempcpy ,
-which are extensions.
+first introduced by
+.Ox ,
+conform to
+.St -p1003.1-2024 .
+.Pp
+Function
+.Fn wmempcpy
+is a
+.Tn GNU
+extensions.
+
diff --git a/sys/sys/cdefs.h b/sys/sys/cdefs.h
index d0a34f1bdfc0..22e2888d80af 100644
--- a/sys/sys/cdefs.h
+++ b/sys/sys/cdefs.h
@@ -227,6 +227,27 @@
__typeof(((void)0, (expr))), t), yes, no)
#endif
+/*
+ * __qualsel() is the building block for C23 qualifier-preserving macros
+ * as proposed in N3020: it selects cexpr when the pointer expression p
+ * references a const-qualified object, and expr otherwise.
+ *
+ * The conditional operator's composite-type rule collapses every
+ * pointer-to-const-object type onto the single "const void *"" association,
+ * so callers passing any such pointer are matched even though _Generic()
+ * otherwise compares types exactly.
+ *
+ * The (__uintptr_t) round-trip only suppresses -Wcast-qual on the throwaway
+ * second operand.
+ */
+#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) || \
+ __has_extension(c_generic_selections)
+#define __qualsel(p, cexpr, expr) \
+ _Generic(1 ? (p) : (void *)(__uintptr_t)(p), \
+ const void *: (cexpr), \
+ default: (expr))
+#endif
+
/*
* C99 Static array indices in function parameter declarations. Syntax such as:
* void bar(int myArray[static 10]);