Add strprefix() and fix a bug with it
Subsystems (5)
v3 cover Add strprefix() and fix a bug with it
Hi!
str_has_prefix() is based on sizes and lengths, which means it must overload the valid length 0 to mean not found. This is a typical consequence of choosing to design an API around sizes and lengths, and vanishes when moving to pointer-based similar APIs.
According to its author (Steven R.), str_has_prefix() was never meant to accept an empty prefix, but that's not documented, and there's no obvious reason why that would be acceptable for a generic string function (as opposed to a function very specific to its original use). Users of strings need a function whose behavior extends naturally to the empty string, and we can do better, by returning a pointer. And indeed, users have ended up using str_has_prefix() with empty prefix, expecting it to work (as is natural of a generic string API).
This patch set adds strprefix(), deprecates str_has_prefix(), and uses strprefix() in one place to fix a bug reported by Bartosz.
We can later work on replacing the other uses of str_has_prefix(), which might hide similar bugs.
This patch set also adds infrastructure to make string functions return a pointer that is as qualified as the input pointer.
Changes in v2
~~~~~~~~~~~~~
- Copy the description from str_has_prefix(). [Steve] - Add links for the Reported-by tags. - Make these APIs const-generic. [Geert]
I didn't apply some changes requested by reviewers:
- I didn't remove the deprecation of str_has_prefix(), because I think it should not be used in new code. It remains useful for code that is already using it, because rewriting all the surrounding code to use pointers instead of offsets might be too risky. However, for new code, strprefix() produces simpler code, and is safer, so I don't see why str_has_prefix() would be good in any new code.
- I didn't rename strprefix() to strsuffix(), because that name would be worse (strsuffix() is widely implemented with other semantics, and having it different here would lead to confusion and bugs). Andy agreed.
See range-diff below.
Testing
~~~~~~~
I haven't changed the implementation of the function, so haven't re-run tests. Since the changes since v2 are only about type safety, with a new wrapper macro, I have built again with 'allmodconfig', just to make sure it still compiles.
Have a lovely day!
Alex
v1: <https://lore.kernel.org/linux-hardening/[email protected]/>
v2: <https://lore.kernel.org/linux-hardening/[email protected]/>
Alejandro Colomar (5):
string: Add strprefix(), and deprecate str_has_prefix() device property: Fix fwnode_name_eq() for empty node names
stddef: Add const_cast()
string: Add [C]QChar_of() and QVoid_of()
string: Make strprefix() const-qualifier-generic
drivers/base/property.c | 5 +--- include/linux/stddef.h | 12 ++++++++ include/linux/string.h | 63 +++++++++++++++++++++++++++++++++++++--- lib/tests/string_kunit.c | 9 ++++++ 4 files changed, 81 insertions(+), 8 deletions(-)
Range-diff against v2:
1: d3b325ecefd1 ! 1: 556766412884 string: Add strprefix(), and deprecate str_has_prefix()
@@ Commit message
We'll slowly migrate existing code from str_has_prefix() to strprefix().
+ Copy the description from str_has_prefix().
+
+ Link: https://lore.kernel.org/linux-hardening/[email protected]/
## include/linux/string.h ##
@@ include/linux/string.h: void memcpy_and_pad(void *dest, size_t dest_len, const void *src, size_t count,
- sizeof(*(obj)) - offsetof(typeof(*(obj)), member)); \
})
-+/**
+ /**
+- * str_has_prefix - Test if a string has a given prefix
+- * @str: The string to test
+- * @prefix: The string to see if @str starts with
+ * strprefix - string prefix
+ * @s: The string to be tested
+ * @prefix: The string to see if @s starts with
-+ *
-+ * Returns:
-+ * * @s+strlen(@prefix) if @s starts with @prefix
-+ * * NULL if @s does not start with @prefix
-+ */
-+static __always_inline char *strprefix(const char *s, const char *prefix) -+{
-+ size_t len = strlen(prefix);
-+ return strncmp(s, prefix, len) == 0 ? (char *) s + len : NULL;
-+}
-+
- /**
- * str_has_prefix - Test if a string has a given prefix
- * @str: The string to test
- * @prefix: The string to see if @str starts with
*
-+ * DEPRECATED: Use strprefix()
-+ *
* A common way to test a prefix of a string is to do:
* strncmp(str, prefix, sizeof(prefix) - 1)
*
* But this can lead to bugs due to typos, or if prefix is a pointer
- * and not a constant. Instead use str_has_prefix().
++ * and not a constant. Instead use strprefix().
++ *
++ * Returns:
++ * * @s+strlen(@prefix) if @s starts with @prefix
++ * * NULL if @s does not start with @prefix
++ */
++static inline char *strprefix(const char *s, const char *prefix)
++{
++ size_t len = strlen(prefix);
++ return strncmp(s, prefix, len) == 0 ? (char *) s + len : NULL;
++}
++
++/**
++ * str_has_prefix - Test if a string has a given prefix
++ * @str: The string to test
++ * @prefix: The string to see if @str starts with
++ *
++ * DEPRECATED: Use strprefix()
++ *
++ * A common way to test a prefix of a string is to do:
++ * strncmp(str, prefix, sizeof(prefix) - 1)
++ *
++ * But this can lead to bugs due to typos, or if prefix is a pointer
+ * and not a constant. Instead use strprefix().
*
* Returns:
2: ac9728afa027 ! 2: eed085c85f36 device property: Fix fwnode_name_eq() for empty node names
@@ Commit message
+ Link: https://lore.kernel.org/linux-hardening/[email protected]/
-: ------------ > 3: 2bf5cf7baad8 stddef: Add const_cast() -: ------------ > 4: 415453a31ebf string: Add [C]QChar_of() and QVoid_of() -: ------------ > 5: 35bf6672f84f string: Make strprefix() const-qualifier-generic
Signature · 1 line
2.55.0
PATCH v3 1/5 string: Add strprefix(), and deprecate str_has_prefix()
As Bartosz reported, str_has_prefix() has a bug: it can't differentiate between an empty prefix matching an empty string from a non-matching prefix. This is a fundamental design flaw in this API, and not something that can be fixed.
As always, this is a consequence of choosing sizes instead of pointers. Sizes are often the wrong choice. In this specific case, pointers are more capable of differentiating these cases, as NULL is a special sentinel value that doesn't need to overload a valid value.
Let's implement strprefix(), a function that returns a pointer right after the prefix if found, and a null pointer if not found.
We'll slowly migrate existing code from str_has_prefix() to strprefix().
Copy the description from str_has_prefix().
--- include/linux/string.h | 31 +++++++++++++++++++++++++++---- lib/tests/string_kunit.c | 9 +++++++++ 2 files changed, 36 insertions(+), 4 deletions(-)
include/linux/string.h +27 -4
@@ -546,15 +546,38 @@ void memcpy_and_pad(void *dest, size_t dest_len, const void *src, size_t count, })/** -* str_has_prefix - Test if a string has a given prefix -* @str: The string to test -* @prefix: The string to see if @str starts with +* strprefix - string prefix +* @s: The string to be tested +* @prefix: The string to see if @s starts with * * A common way to test a prefix of a string is to do: * strncmp(str, prefix, sizeof(prefix) - 1) * * But this can lead to bugs due to typos, or if prefix is a pointer -* and not a constant. Instead use str_has_prefix(). +* and not a constant. Instead use strprefix(). +* +* Returns: +* * @s+strlen(@prefix) if @s starts with @prefix +* * NULL if @s does not start with @prefix +*/ +static inline char *strprefix (const char *s,const char *prefix) +{ +size_t len =strlen (prefix); +return strncmp (s, prefix, len) ==0 ? (char *) s + len :NULL ; +} + +/** +* str_has_prefix - Test if a string has a given prefix +* @str: The string to test +* @prefix: The string to see if @str starts with +* +* DEPRECATED: Use strprefix() +* +* A common way to test a prefix of a string is to do: +* strncmp(str, prefix, sizeof(prefix) - 1) +* +* But this can lead to bugs due to typos, or if prefix is a pointer +* and not a constant. Instead use strprefix(). * * Returns: * * strlen(@prefix) if @str starts with @prefix
lib/tests/string_kunit.c +9 -0
@@ -287,6 +287,14 @@ static void string_test_strspn(struct kunit *test) } } +static void string_test_strprefix (struct kunit *test) +{ +KUNIT_ASSERT_EQ (test,0 ,strcmp (strprefix ("foobar" ,"foo" ),"bar" )); +KUNIT_ASSERT_EQ (test,0 ,strcmp (strprefix ("foobar" ,"" ),"foobar" )); +KUNIT_ASSERT_EQ (test,0 ,strcmp (strprefix ("foobar" ,"foobar" ),"" )); +KUNIT_ASSERT_NULL (test,strprefix ("foobar" ,"bar" )); +} +static char strcmp_buffer1[STRCMP_LARGE_BUF_LEN ];static char strcmp_buffer2[STRCMP_LARGE_BUF_LEN ];
@@ -891,6 +899,7 @@ static struct kunit_case string_test_cases[] = {KUNIT_CASE (string_test_strnchr),KUNIT_CASE (string_test_strrchr),KUNIT_CASE (string_test_strspn), +KUNIT_CASE (string_test_strprefix),KUNIT_CASE (string_test_strcmp),KUNIT_CASE (string_test_strcmp_long_strings), KUNIT_CASE(string_test_strncmp),
Signature · 1 line
2.55.0
Re: [PATCH v3 1/5] string: Add strprefix(), and deprecate str_has_prefix()
Quoted 10 lines · Alejandro Colomar
As Bartosz reported,
str_has_prefix()has a bug: it can't differentiate between an empty prefix matching an empty string from a non-matching prefix. This is a fundamental design flaw in this API, and not something that can be fixed.As always, this is a consequence of choosing sizes instead of pointers. Sizes are often the wrong choice. In this specific case, pointers are more capable of differentiating these cases, as NULL is a special sentinel value that doesn't need to overload a valid value.
Quoted 4 lines
Let's implement
strprefix(), a function that returns a pointer right after the prefix if found, and a null pointer if not found.We'll slowly migrate existing code from
str_has_prefix()tostrprefix().
Most uses of str_has_prefix could use the existing strstarts.
Re: [PATCH v3 1/5] string: Add strprefix(), and deprecate str_has_prefix()
Hi Joe,
Quoted 14 lines
Date: 2026-11-10 07:47:55-0800
From: Joe Perches <[email protected]>Quoted 10 lines · Alejandro Colomar
As Bartosz reported,
str_has_prefix()has a bug: it can't differentiate between an empty prefix matching an empty string from a non-matching prefix. This is a fundamental design flaw in this API, and not something that can be fixed.As always, this is a consequence of choosing sizes instead of pointers. Sizes are often the wrong choice. In this specific case, pointers are more capable of differentiating these cases, as NULL is a special sentinel value that doesn't need to overload a valid value.
Quoted 6 lines
Quoted 4 lines
Let's implement
strprefix(), a function that returns a pointer right after the prefix if found, and a null pointer if not found.We'll slowly migrate existing code from
str_has_prefix()tostrprefix().Most uses of
str_has_prefixcould use the existing strstarts.
Hmmm, actually, both str_has_prefix() and strstarts() can be replaced by strprefix(), which provides functionality for both use cases. It's the more generic API for this. And I expect compilers can optimize the unused parts if actually unused.
Have a lovely day!
Alex
Signature · 1 line
<https://www.alejandro-colomar.es>
Re: [PATCH v3 1/5] string: Add strprefix(), and deprecate str_has_prefix()
In reply to Alejandro ColomarQuoted 6 lines · On Tue, 10 Nov 2026 16:33:23 +0100 Alejandro Colomar <[email protected]> wrote:
+/**
+ *str_has_prefix- Test if a string has a given prefix
+ * @str: The string to test
+ * @prefix: The string to see if @str starts with
+ *
+ * DEPRECATED: Usestrprefix()
I'm still not convinced, so NAK on this part.
-- Steve
Quoted 7 lines
+ *
+ * A common way to test a prefix of a string is to do:
+ *strncmp(str, prefix, sizeof(prefix) - 1)
+ *
+ * But this can lead to bugs due to typos, or if prefix is a pointer
+ * and not a constant. Instead usestrprefix().
*
PATCH v3 2/5 device property: Fix fwnode_name_eq() for empty node names
In reply to Alejandro Colomarstr_has_prefix() returns the prefix length on match and 0 on mismatch, so the mistmatch case can't be told from "empty prefix matched an empty string".
For instance: the OF root node's name as returned by fwnode_get_name() is "" and will return 0 for any name matched against it and since len is 0 too, fwnode_name_eq() will return true.
Let's use strprefix() instead, which also makes this function simpler.
--- drivers/base/property.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-)
drivers/base/property.c +1 -4
@@ -692,15 +692,12 @@ const char *fwnode_get_name_prefix(const struct fwnode_handle *fwnode)bool fwnode_name_eq (const struct fwnode_handle *fwnode,const char *name) {const char *node_name; -ptrdiff_t len; node_name =fwnode_get_name (fwnode);if (!node_name)return false; - len =strchrnul (node_name,'@' ) - node_name; - -return str_has_prefix (node_name, name) == len; +return strprefix (node_name, name) ==strchrnul (node_name,'@' ); }EXPORT_SYMBOL_GPL (fwnode_name_eq);
Signature · 1 line
2.55.0
PATCH v3 3/5 stddef: Add const_cast()
In reply to Alejandro ColomarThis macro serves to cast const away without risking unwanted conversions due to mistakes in the cast.
It's similar to C++'s const_cast(), but it is more restrictive: it can only cast away 'const', and not arbitrary qualifiers.
--- include/linux/stddef.h | 12 ++++++++++++ 1 file changed, 12 insertions(+)
include/linux/stddef.h +12 -0
@@ -194,4 +194,16 @@ enum { #define TRAILING_OVERLAP(TYPE, NAME, FAM, MEMBERS) \ __TRAILING_OVERLAP(TYPE, NAME, FAM, /* no attrs */, MEMBERS) +/** +* const_cast() - Drop const in the left-most derivation of a pointer +* +* @T: Type for the cast. +* @p: Pointer to be casted. +* +* If @p is of type const @T, then it is casted to @T. +* Otherwise, @p is returned without any casts. +* This avoids mistakes that change anything other than 'const' in a cast. +*/ +#define const_cast (T , p) _Generic(p, const T: (T) (p), default: (p)) +#endif
Signature · 1 line
2.55.0
Re: [PATCH v3 3/5] stddef: Add const_cast()
Quoted 20 lines · On Tue, 10 Nov 2026 16:33:40 +0100 Alejandro Colomar <[email protected]> wrote:
This macro serves to cast const away without risking unwanted conversions due to mistakes in the cast.
It's similar to C++'s
const_cast(), but it is more restrictive: it can only cast away 'const', and not arbitrary qualifiers.Signed-off-by Alejandro Colomar <[email protected]>--- include/linux/stddef.h | 12 ++++++++++++ 1 file changed, 12 insertions(+)include/linux/stddef.h +1
-0@@ -194,4 +194,16 @@ enum { #define TRAILING_OVERLAP(TYPE, NAME, FAM, MEMBERS) \ __TRAILING_OVERLAP(TYPE, NAME, FAM, /* no attrs */, MEMBERS) +/**
include/linux/stddef.h enum { · line 207
Quoted 10 lines
include/linux/stddef.h +10
-0+* const_cast() - Drop const in the left-most derivation of a pointer +* +* @T: Type for the cast. +* @p: Pointer to be casted. +* +* If @p is of type const @T, then it is casted to @T. +* Otherwise, @p is returned without any casts. +* This avoids mistakes that change anything other than 'const' in a cast. +*/ +#define const_cast (T , p) _Generic(p, const T: (T) (p), default: (p))
Should it be de_const_cast(), or just de_const(). It is a shame you need to specify the type, but why not:
_Generic(p, T: p, const T: (T)(p))
so that you get a compile time error from the _Generic() if the type is wrong.
David
Quoted 2 lines
include/linux/stddef.h +1
-0+#endif
Re: [PATCH v3 3/5] stddef: Add const_cast()
Hi David,
Quoted 25 lines
Date: 2026-11-11 10:25:35+0000
From: David Laight <[email protected]>Quoted 19 lines · On Tue, 10 Nov 2026 16:33:40 +0100 Alejandro Colomar <[email protected]> wrote:
This macro serves to cast const away without risking unwanted conversions due to mistakes in the cast.
It's similar to C++'s
const_cast(), but it is more restrictive: it can only cast away 'const', and not arbitrary qualifiers.Signed-off-by Alejandro Colomar <[email protected]>--- include/linux/stddef.h | 12 ++++++++++++ 1 file changed, 12 insertions(+)include/linux/stddef.h +0
-0@@ -194,4 +194,16 @@ enum { #define TRAILING_OVERLAP(TYPE, NAME, FAM, MEMBERS) \ __TRAILING_OVERLAP(TYPE, NAME, FAM, /* no attrs */, MEMBERS)
Quoted 13 lines
include/linux/stddef.h enum { · line 207
Quoted 11 lines
include/linux/stddef.h +11
-0+/** +* const_cast() - Drop const in the left-most derivation of a pointer +* +* @T: Type for the cast. +* @p: Pointer to be casted. +* +* If @p is of type const @T, then it is casted to @T. +* Otherwise, @p is returned without any casts. +* This avoids mistakes that change anything other than 'const' in a cast. +*/ +#define const_cast (T , p) _Generic(p, const T: (T) (p), default: (p))Should it be
de_const_cast(), or justde_const().
If we want to be explicit about it being for dropping, perhaps drop_const() or cast_drop_const() feels more readable to me than de_const(). I had been calling this const_cast() for following C++'s name.
Quoted 1 line
It is a shame you need to specify the type,
In some cases we don't want to drop const; it's a "maybe drop const", actually. Perhaps for this reason, cast_drop_const() might be misleading, in that it tells you that it dropped const, but sometimes it didn't. On the other hand, if you thougt you had dropped const and haven't, you'll get build errors, so it's not a big problem.
The reason why we may want to not drop const is that since we do:
const_cast(CQChar_of(s) *, strprefix(s, pfx))
'CQChar_of() *' expands to either 'const char *' or 'char *', depending on the qualification of 's'. In case it expands to 'const char *', we want to retain const, and not drop it. The most accurate name would be cast_at_most_drop_const_but_maybe_not(). :)
Quoted 4 lines
but why not:
_Generic(p, T: p, const T: (T)(p))so that you get a compile time error from the
_Generic()if the type is wrong.
Thanks! I like that being more robust.
Have a lovely day!
Alex
Signature · 1 line
<https://www.alejandro-colomar.es>
PATCH v3 4/5 string: Add [C]QChar_of() and QVoid_of()
In reply to Alejandro ColomarThese macros serve to implement qualifier-generic macros as those specified in ISO C23.
--- include/linux/string.h | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+)
include/linux/string.h +30 -0
@@ -14,6 +14,36 @@#include <linux/stdarg.h> #include <uapi/linux/string.h> +/** +* QVoid_of - qualified void of +* +* @p: Pointer to test for qualifiers +* +* This macro produces the type name 'void', +* qualified with any qualifiers present in the element of @p. +*/ +#define QVoid_of (p) typeof((__QVoidptr_of(p)){0}[0]) +#define __QVoidptr_of (p) typeof(1?(p):(void*){0}) + +/** +* QChar_of - qualified char of +* +* @Q: Qualifier to test. +* @p: Pointer to test for the @Q qualifier +* +* This macro produces the type name 'char', +* @Q-qualified iff the input pointer points to a @Q-qualified element. +*/ +#define QChar_of (Q , p) typeof \ +( \ + _Generic((QVoid_of(p) *){0}, \ + Q void *: (Q char){0}, \ + void *: ( char){0} \ + ) \ +) +// CQChar_of - const qualified char of +#define CQChar_of (p) QChar_of(const, p) +extern char *strndup_user (const char __user *,long );extern void *memdup_user (const void __user *,size_t )__realloc_size (2 );extern void *vmemdup_user (const void __user *,size_t )__realloc_size (2 );
Signature · 1 line
2.55.0
[email protected]
AI reviewRe: [PATCH v3 4/5] string: Add [C]QChar_of() and QVoid_of()
Thank you for your contribution! Sashiko AI review found 2 potential issues to consider: - [High] string: void pointer subscript in QVoid_of() breaks Clang builds - [Medium] string: QChar_of() fails…
Thank you for your contribution! Sashiko AI review found 2 potential issues to consider:
- [High] string: void pointer subscript in QVoid_of() breaks Clang builds
- [Medium] string: QChar_of() fails to compile when pointee has qualifiers other than Q
--
commit cea37634307d89ede7b6ca6128f006d50a90391c
Author: Alejandro Colomar <[email protected]>
string: Add [C]QChar_of() and QVoid_of()
This commit introduces the QVoid_of(), QChar_of(), and CQChar_of() helper macros in include/linux/string.h to support qualifier-generic string macros.
Quoted 8 lines
include/linux/string.h +0
-0@@ -14,6 +14,36 @@#include <linux/stdarg.h> #include <uapi/linux/string.h>
include/linux/string.h line 26
Quoted 10 lines
include/linux/string.h +10
-0+/** +* QVoid_of - qualified void of +* +* @p: Pointer to test for qualifiers +* +* This macro produces the type name 'void', +* qualified with any qualifiers present in the element of @p. +*/ +#define QVoid_of (p) typeof((__QVoidptr_of(p)){0}[0]) +#define __QVoidptr_of (p) typeof(1?(p):(void*){0})
[Severity: High]
Does applying the array subscript [0] to a void pointer in QVoid_of() break Clang builds?
__QVoidptr_of(p) produces a pointer to (qualified) void. Because array subscripting requires a pointer to a complete object type, Clang rejects (__QVoidptr_of(p)){0}[0] even inside typeof() with an incomplete type error, and GCC emits a -Wpointer-arith warning when pointer arithmetic warnings are enabled. Using unary * instead of [0], such as typeof(*(__QVoidptr_of(p)){0}), avoids both issues.
Quoted 7 lines
include/linux/string.h +7
-0+ +/** +* QChar_of - qualified char of +* +* @Q: Qualifier to test. +* @p: Pointer to test for the @Q qualifier +*
include/linux/string.h line 43
Quoted 10 lines
include/linux/string.h +10
-0+* This macro produces the type name 'char', +* @Q-qualified iff the input pointer points to a @Q-qualified element. +*/ +#define QChar_of (Q , p) typeof \ +( \ + _Generic((QVoid_of(p) *){0}, \ + Q void *: (Q char){0}, \ + void *: ( char){0} \ + ) \ +)
[Severity: Medium]
Can QChar_of() fail to compile when the pointed-to type of p carries a qualifier other than Q?
QVoid_of(p) preserves all qualifiers on the target of p, but the _Generic selection only matches exact Q void * and unqualified void *. Passing a volatile char * or const volatile char * to CQChar_of(p) produces a volatile void * or const volatile void * controlling expression, which matches neither association and fails at compile time. Matching against Q QVoid_of(p) *: (Q char){0} and default: (char){0} would test specifically for Q regardless of other qualifiers.
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=4
Re: [PATCH v3 4/5] string: Add [C]QChar_of() and QVoid_of()
Quoted 7 lines
Date: 2026-11-10 15:45:13+0000
From: [email protected]Thank you for your contribution! Sashiko AI review found 2 potential issues to consider:
- [High] string: void pointer subscript inQVoid_of()breaks Clang builds
It seems I can't build with LLVM in the way that sashiko suggested in the other mail, so I can't reproduce this:
$ make LLVM=1 SYNC include/config/auto.conf HOSTCC scripts/basic/fixdep clang: error: invalid linker name in argument '-fuse-ld=lld' make[3]: *** [scripts/Makefile.host:114: scripts/basic/fixdep] Error 1 make[2]: *** [Makefile:682: scripts_basic] Error 2 make[1]: *** [/srv/alx/src/linux/linux/llvm/Makefile:912: include/config/auto.conf] Error 2 make[1]: *** [include/config/auto.conf] Deleting file 'include/generated/rustc_cfg' make[1]: *** [include/config/auto.conf] Deleting file 'include/generated/autoconf.h' make: *** [Makefile:248: __sub-make] Error 2
But I see no reason why Clang shouldn't build this. I've written a small test program just to see, and Clang seems to just work well:
alx@devuan:~/tmp$ cat const.c #include <stddef.h>
#define QVoid_of(p) typeof((__QVoidptr_of(p)){0}[0])
#define __QVoidptr_of(p) typeof(1?(p):(void*){0})
int
main(void)
{
const void *p;
QVoid_of(p) *q; // const void *
}
alx@devuan:~/tmp$ gcc -Wall -Wextra -Wno-unused const.c
alx@devuan:~/tmp$ clang -Wall -Wextra -Wno-unused const.c
alx@devuan:~/tmp$ Unless anyone can actually reproduce the build issue, I'll assume this report is nonsense.
Quoted 2 lines
- [Medium] string:
QChar_of()fails to compile when pointee has qualifiers other than Q
That's actually desirable. You don't want to call strchr(3) with a pointer to volatile.
Quoted 11 lines
--
commit
cea37634307d89ede7b6ca6128f006d50a90391c
Author: Alejandro Colomar <[email protected]>string: Add [C]QChar_of() and
QVoid_of()This commit introduces the
QVoid_of(),QChar_of(), andCQChar_of()helper macros ininclude/linux/string.hto support qualifier-generic string macros.Quoted 1 line
include/linux/string.h +0
-0
[...]
Quoted 4 lines
[Severity: High]
Does applying the array subscript [0] to a void pointer inQVoid_of()break Clang builds?
I don't see why it would. I can't use LLVM myself, so can't reproduce, but a small test program shows that Clang works just fine with it.
Quoted 6 lines
__QVoidptr_of(p)produces a pointer to (qualified) void. Because array subscripting requires a pointer to a complete object type, Clang rejects (__QVoidptr_of(p)){0}[0] even insidetypeof()with an incomplete type error, and GCC emits a -Wpointer-arith warning when pointer arithmetic warnings are enabled. Using unary * instead of [0], such as typeof(*(__QVoidptr_of(p)){0}), avoids both issues.
This is a lie, unless anyone can confirm.
[...]
Quoted 3 lines
[Severity: Medium]
CanQChar_of()fail to compile when the pointed-to type of p carries a qualifier other than Q?
Indeed, a desirable property.
Quoted 7 lines
QVoid_of(p)preserves all qualifiers on the target of p, but the_Genericselection only matches exact Q void * and unqualified void *. Passing a volatile char * or const volatile char * toCQChar_of(p)produces a volatile void * or const volatile void * controlling expression, which matches neither association and fails at compile time. Matching against QQVoid_of(p)*: (Q char){0} and default: (char){0} would test specifically for Q regardless of other qualifiers.
You certainly don't want to pass a volatile char* to strchr(3).
Alex
Signature · 1 line
<https://www.alejandro-colomar.es>
PATCH v3 5/5 string: Make strprefix() const-qualifier-generic
In reply to Alejandro Colomar--- include/linux/string.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
include/linux/string.h +3 -1
@@ -590,7 +590,9 @@ void memcpy_and_pad(void *dest, size_t dest_len, const void *src, size_t count,* * @s+strlen(@prefix) if @s starts with @prefix * * NULL if @s does not start with @prefix */ -static inline char *strprefix (const char *s,const char *prefix) +#define strprefix (s, pfx) const_cast(CQChar_of(s) *, strprefix(s, pfx)) + +static inline char *(strprefix)(const char *s,const char *prefix) {size_t len =strlen (prefix);return strncmp (s, prefix, len) ==0 ? (char *) s + len :NULL ;
Signature · 1 line
2.55.0
[email protected]
AI reviewRe: [PATCH v3 5/5] string: Make strprefix() const-qualifier-generic
Thank you for your contribution! Sashiko AI review found 3 potential issues to consider: - [High] string: strprefix() breaks Clang builds via void * subscript in QVoid_of() - [Medium] string…
Thank you for your contribution! Sashiko AI review found 3 potential issues to consider:
- [High] string: strprefix() breaks Clang builds via void * subscript in QVoid_of()
- [Medium] string: strprefix() always returns char * due to non-const (strprefix)()
- [Low] string: strprefix() macro parameter pfx mismatches @prefix kernel-doc
--
commit f882ec908122ee0c3f780648b859ed41700664d0
Author: Alejandro Colomar <[email protected]>
string: Make strprefix() const-qualifier-generic
This commit wraps strprefix() in a const_cast(CQChar_of(s) *, ...) macro so that its return type preserves the const qualifier of its first argument.
include/linux/string.h void memcpy_and_pad(void *dest, size_t dest_len, const void *src, size_t count, · line 580
Quoted 10 lines
include/linux/string.h +1
-1@@ -577,7 +577,9 @@ void memcpy_and_pad(void *dest, size_t dest_len, const void *src, size_t count,* * @s+strlen(@prefix) if @s starts with @prefix * * NULL if @s does not start with @prefix */ -static inline char *strprefix (const char *s,const char *prefix) +#define strprefix (s, pfx) const_cast(CQChar_of(s) *, strprefix(s, pfx))
[Severity: High]
Does expanding CQChar_of(s) here break Clang builds (make LLVM=1)?
CQChar_of(s) expands to QChar_of(const, s), which expands QVoid_of(s):
include/linux/string.h:
#define QVoid_of(p) typeof((__QVoidptr_of(p)){0}[0])
#define __QVoidptr_of(p) typeof(1?(p):(void*){0})
Because __QVoidptr_of(p) is void * or const void *, applying the [0] array subscript operator to it fails to compile under Clang ("error: subscript of a pointer to void") in every translation unit that calls strprefix(), such as drivers/base/property.c, and triggers -Wpointer-arith under W=3 in GCC.
[Severity: Low]
Does naming the macro parameter pfx instead of prefix cause scripts/kernel-doc warnings? The kernel-doc comment immediately above documents @prefix, and scripts/kernel-doc binds that comment to the macro definition that follows it.
include/linux/string.h void memcpy_and_pad(void *dest, size_t dest_len, const void *src, size_t count, · line 598
Quoted 5 lines
include/linux/string.h +2
-0+ +static inline char *(strprefix)(const char *s,const char *prefix) {size_t len =strlen (prefix);return strncmp (s, prefix, len) ==0 ? (char *) s + len :NULL ;
[Severity: Medium]
Does strprefix(s, pfx) still unconditionally return char * even when s is const char *?
include/linux/stddef.h:
#define const_cast(T, p) _Generic(p, const T: (T) (p), default: (p))
In const_cast(CQChar_of(s) *, strprefix(s, pfx)), the first _Generic association is const CQChar_of(s) *, which is const char *. Because the underlying (strprefix)() function returns char *, the controlling expression never matches const char * and always selects the default branch, returning an unqualified char * and stripping const from read-only strings. Should (strprefix)() return const char * and avoid casting s to char *?
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=5
Re: [PATCH v3 5/5] string: Make strprefix() const-qualifier-generic
Quoted 7 lines
Date: 2026-11-10 15:44:36+0000
From: [email protected]Thank you for your contribution! Sashiko AI review found 3 potential issues to consider:
- [High] string:strprefix()breaks Clang builds via void * subscript inQVoid_of()
This seems nonsense; already replied to in the review of 4/5.
Quoted 2 lines
- [Medium] string:
strprefix()always returns char * due to non-const (strprefix)()
Hmmm. Fixed for v4.
Quoted 2 lines
- [Low] string:
strprefix()macro parameter pfx mismatches @prefix kernel-doc
Hmmm. Fixed for v4.
Quoted 10 lines
--
commit
f882ec908122ee0c3f780648b859ed41700664d0
Author: Alejandro Colomar <[email protected]>string: Make
strprefix()const-qualifier-genericThis commit wraps
strprefix()in aconst_cast(CQChar_of(s) *, ...)macro so that its return type preserves the const qualifier of its first argument.
Quoted 13 lines
include/linux/string.h void memcpy_and_pad(void *dest, size_t dest_len, const void *src, size_t count, · line 580
Quoted 10 lines
include/linux/string.h +1
-1@@ -577,7 +577,9 @@ void memcpy_and_pad(void *dest, size_t dest_len, const void *src, size_t count,* * @s+strlen(@prefix) if @s starts with @prefix * * NULL if @s does not start with @prefix */ -static inline char *strprefix (const char *s,const char *prefix) +#define strprefix (s, pfx) const_cast(CQChar_of(s) *, strprefix(s, pfx))[Severity: High]
Does expandingCQChar_of(s)here break Clang builds (make LLVM=1)?
I can't even seem to build with LLVM=1, so can't reproduce, but as said in the other subthread (4/5), this makes no sense. Clang accepts this code just fine.
Quoted 10 lines
CQChar_of(s)expands toQChar_of(const, s), which expandsQVoid_of(s):
include/linux/string.h:
#defineQVoid_of(p)typeof((__QVoidptr_of(p)){0}[0])
#define__QVoidptr_of(p)typeof(1?(p):(void*){0})Because
__QVoidptr_of(p)is void * or const void *, applying the [0] array subscript operator to it fails to compile under Clang ("error: subscript of a pointer to void") in every translation unit that callsstrprefix(), such asdrivers/base/property.c, and triggers -Wpointer-arith under W=3 in GCC.
Hmmmm, I see why it says Clang may not compile. For that to happen, one needs to enable non-usual diagnostics.
alx@devuan:~/tmp$ cat const.c #include <stddef.h>
#define QVoid_of(p) typeof((__QVoidptr_of(p)){0}[0])
#define __QVoidptr_of(p) typeof(1?(p):(void*){0})
int
main(void)
{
const void *p;
QVoid_of(p) *q; // const void *
}
alx@devuan:~/tmp$ gcc -Wall -Wextra -Wpointer-arith -Wno-unused const.c
const.c: In function ‘main’:
const.c:3:55: warning: pointer of type ‘void *’ used in arithmetic [-Wpointer-arith]
3 | #define QVoid_of(p) typeof((__QVoidptr_of(p)){0}[0])
| ^
const.c:10:9: note: in expansion of macro ‘QVoid_of’
10 | QVoid_of(p) *q; // const void *
| ^~~~~~~~I think this diagnostic isn't very useful, which is why it isn't in -Wall -Wextra.
Quoted 4 lines
[Severity: Low]
Does naming the macro parameter pfx instead of prefix causescripts/kernel-docwarnings? The kernel-doc comment immediately above documents @prefix, andscripts/kernel-docbinds that comment to the macro definition that follows it.
Solved.
Quoted 13 lines
include/linux/string.h void memcpy_and_pad(void *dest, size_t dest_len, const void *src, size_t count, · line 598
Quoted 5 lines
include/linux/string.h +2
-0+ +static inline char *(strprefix)(const char *s,const char *prefix) {size_t len =strlen (prefix);return strncmp (s, prefix, len) ==0 ? (char *) s + len :NULL ;[Severity: Medium]
Doesstrprefix(s, pfx)still unconditionally return char * even when s is const char *?
include/linux/stddef.h:
#defineconst_cast(T, p)_Generic(p, const T: (T) (p), default: (p))
Quoted 6 lines
In
const_cast(CQChar_of(s) *, strprefix(s, pfx)), the first_Genericassociation is constCQChar_of(s)*, which is const char *. Because the underlying (strprefix)() function returns char *, the controlling expression never matches const char * and always selects the default branch, returning an unqualified char * and stripping const from read-only strings. Should (strprefix)() return const char * and avoid casting s to char *?
Solved.
Quoted 3 lines
Signature · 2 lines
-- Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=5
Signature · 1 line
<https://www.alejandro-colomar.es>
Re: [PATCH v3 5/5] string: Make strprefix() const-qualifier-generic
In reply to Alejandro ColomarOn Tue, 2026-11-10 at 16:33 +0100, Alejandro Colomar wrote:
no commit description on proposed patch
Quoted 11 lines
Suggested-by Geert Uytterhoeven <[email protected]>Signed-off-by Alejandro Colomar <[email protected]>--- include/linux/string.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)include/linux/string.h +0
-0
include/linux/string.h void memcpy_and_pad(void *dest, size_t dest_len, const void *src, size_t count, · line 595
Quoted 7 lines
include/linux/string.h +3
-1@@ -590,7 +590,9 @@ void memcpy_and_pad(void *dest, size_t dest_len, const void *src, size_t count,* * @s+strlen(@prefix) if @s starts with @prefix * * NULL if @s does not start with @prefix */ -static inline char *strprefix (const char *s,const char *prefix) +#define strprefix (s, pfx) const_cast(CQChar_of(s) *, strprefix(s, pfx)) + +static inline char *(strprefix)(const char *s,const char *prefix)
Why introduce a new coding style?
Re: [PATCH v3 5/5] string: Make strprefix() const-qualifier-generic
Hi Joe,
Quoted 6 lines
Date: 2026-11-10 08:32:41-0800
From: Joe Perches <[email protected]>On Tue, 2026-11-10 at 16:33 +0100, Alejandro Colomar wrote:
no commit description on proposed patch
There isn't much to say, but I'll add something (the explanation of why the parentheses are needed around the function name, for example).
Quoted 6 lines
Quoted 6 lines
Suggested-by Geert Uytterhoeven <[email protected]>Signed-off-by Alejandro Colomar <[email protected]>--- include/linux/string.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
Quoted 14 lines
include/linux/string.h void memcpy_and_pad(void *dest, size_t dest_len, const void *src, size_t count, · line 595
Quoted 12 lines
include/linux/string.h +3
-1@@ -590,7 +590,9 @@ void memcpy_and_pad(void *dest, size_t dest_len, const void *src, size_t count,* * @s+strlen(@prefix) if @s starts with @prefix * * NULL if @s does not start with @prefix */ -static inline char *strprefix (const char *s,const char *prefix) +#define strprefix (s, pfx) const_cast(CQChar_of(s) *, strprefix(s, pfx)) + +static inline char *(strprefix)(const char *s,const char *prefix)Why introduce a new coding style?
It's not style; the parentheses are technically needed to avoid macro expansion, since the macro has the same name as the function.
Alternatively, I could rename the function to __strprefix().
Have a lovely night!
Alex
Signature · 1 line
<https://www.alejandro-colomar.es>
Re: [PATCH v3 5/5] string: Make strprefix() const-qualifier-generic
Quoted 33 lines · On Wed, 11 Nov 2026 00:27:28 +0100 Alejandro Colomar…
Hi Joe,
Quoted 6 lines
Date: 2026-11-10 08:32:41-0800
From: Joe Perches <[email protected]>On Tue, 2026-11-10 at 16:33 +0100, Alejandro Colomar wrote:
no commit description on proposed patch
There isn't much to say, but I'll add something (the explanation of why the parentheses are needed around the function name, for example).
Quoted 6 lines
Quoted 6 lines
Suggested-by Geert Uytterhoeven <[email protected]>Signed-off-by Alejandro Colomar <[email protected]>--- include/linux/string.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)Quoted 14 lines
include/linux/string.h void memcpy_and_pad(void *dest, size_t dest_len, const void *src, size_t count, · line 595
Quoted 12 lines
include/linux/string.h +3
-1@@ -590,7 +590,9 @@ void memcpy_and_pad(void *dest, size_t dest_len, const void *src, size_t count,* * @s+strlen(@prefix) if @s starts with @prefix * * NULL if @s does not start with @prefix */ -static inline char *strprefix (const char *s,const char *prefix) +#define strprefix (s, pfx) const_cast(CQChar_of(s) *, strprefix(s, pfx)) + +static inline char *(strprefix)(const char *s,const char *prefix)Why introduce a new coding style?
Quoted 4 lines
It's not style; the parentheses are technically needed to avoid macro expansion, since the macro has the same name as the function.
Alternatively, I could rename the function to
__strprefix().
Or put the #define after the function definition.
David
Quoted 5 lines
Have a lovely night!
Alex
Re: [PATCH v3 5/5] string: Make strprefix() const-qualifier-generic
Hi David,
Quoted 39 lines
Date: 2026-11-11 10:28:36+0000
From: David Laight <[email protected]>Quoted 33 lines · On Wed, 11 Nov 2026 00:27:28 +0100 Alejandro Colomar…
Hi Joe,
Quoted 6 lines
Date: 2026-11-10 08:32:41-0800
From: Joe Perches <[email protected]>On Tue, 2026-11-10 at 16:33 +0100, Alejandro Colomar wrote:
no commit description on proposed patch
There isn't much to say, but I'll add something (the explanation of why the parentheses are needed around the function name, for example).
Quoted 20 lines
Quoted 11 lines
Suggested-by Geert Uytterhoeven <[email protected]>Signed-off-by Alejandro Colomar <[email protected]>--- include/linux/string.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)include/linux/string.h +0
-0include/linux/string.h void memcpy_and_pad(void *dest, size_t dest_len, const void *src, size_t count, · line 595
Quoted 7 lines
include/linux/string.h +3
-1@@ -590,7 +590,9 @@ void memcpy_and_pad(void *dest, size_t dest_len, const void *src, size_t count,* * @s+strlen(@prefix) if @s starts with @prefix * * NULL if @s does not start with @prefix */ -static inline char *strprefix (const char *s,const char *prefix) +#define strprefix (s, pfx) const_cast(CQChar_of(s) *, strprefix(s, pfx)) + +static inline char *(strprefix)(const char *s,const char *prefix)Why introduce a new coding style?
Quoted 6 lines
Quoted 4 lines
It's not style; the parentheses are technically needed to avoid macro expansion, since the macro has the same name as the function.
Alternatively, I could rename the function to
__strprefix().Or put the #define after the function definition.
Done for v4.
Cheers,
Alex
Quoted 10 lines
David
Quoted 5 lines
Have a lovely night!
Alex
Signature · 1 line
<https://www.alejandro-colomar.es>
Re: [PATCH v3 5/5] string: Make strprefix() const-qualifier-generic
In reply to Alejandro ColomarQuoted 11 lines · On Tue, 10 Nov 2026 16:33:56 +0100 Alejandro Colomar <[email protected]> wrote:
Suggested-by Geert Uytterhoeven <[email protected]>Signed-off-by Alejandro Colomar <[email protected]>--- include/linux/string.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)include/linux/string.h +0
-0
include/linux/string.h void memcpy_and_pad(void *dest, size_t dest_len, const void *src, size_t count, · line 593
Quoted 5 lines
include/linux/string.h +1
-1@@ -590,7 +590,9 @@ void memcpy_and_pad(void *dest, size_t dest_len, const void *src, size_t count,* * @s+strlen(@prefix) if @s starts with @prefix * * NULL if @s does not start with @prefix */ -static inline char *strprefix (const char *s,const char *prefix) +#define strprefix (s, pfx) const_cast(CQChar_of(s) *, strprefix(s, pfx))
Isn't in enough to do:
#define strprefix(s, pfx) ((typeof(s))strprefix(s, pfx))
which correctly preserves all qualifiers.
David
Quoted 5 lines
include/linux/string.h +2
-0+ +static inline char *(strprefix)(const char *s,const char *prefix) {size_t len =strlen (prefix);return strncmp (s, prefix, len) ==0 ? (char *) s + len :NULL ;
Re: [PATCH v3 5/5] string: Make strprefix() const-qualifier-generic
Hi David,
Quoted 3 lines
Date: 2026-11-11 10:34:38+0000
From: David Laight <[email protected]>
[...]
Quoted 7 lines
include/linux/string.h void memcpy_and_pad(void *dest, size_t dest_len, const void *src, size_t count, · line 593
Quoted 2 lines
include/linux/string.h +1
-1-static inline char *strprefix (const char *s,const char *prefix) +#define strprefix (s, pfx) const_cast(CQChar_of(s) *, strprefix(s, pfx))Isn't in enough to do:
#definestrprefix(s, pfx)((typeof(s))strprefix(s, pfx))which correctly preserves all qualifiers.
This would return a const void* if you pass a const void*. In general, we shouldn't be passing a void* to a string function, but C allows this. With CQChar_of(), you coerce a 'const void *' into a 'const char *'.
See also: <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3998.pdf#subsection.0.7.28.5>
Cheers,
Alex
Signature · 1 line
<https://www.alejandro-colomar.es>