Skip to contentSkip to conversation

Add strprefix() and fix a bug with it

PATCH In review Alejandro Colomar 6 participants 21 messages Nov 10 – Nov 11

Subsystems (5)
lore
Share & tools
lore
#0 lore raw

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

Fixes a9c8c738066b7 ("device property: Add fwnode_name_eq()")

+ 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
#1 lore raw

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
index 6cb5cdd01158..12e31c17f0d3 100644
@@ -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
index 0819ace5b027..73d2614b1474 100644
@@ -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
#2 lore raw

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() to strprefix().

Most uses of str_has_prefix could use the existing strstarts.

#3 lore raw

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() to strprefix().

Most uses of str_has_prefix could 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>
#4 lore raw

Re: [PATCH v3 1/5] string: Add strprefix(), and deprecate str_has_prefix()

In reply to Alejandro Colomar
Quoted 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: Use strprefix()

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 use strprefix().
*

#5 lore raw

PATCH v3 2/5 device property: Fix fwnode_name_eq() for empty node names

In reply to Alejandro Colomar

str_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.

Fixes a9c8c738066b7 ("device property: Add fwnode_name_eq()")
Cc Andy Shevchenko <[email protected]>
---
 drivers/base/property.c | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)
drivers/base/property.c +1 -4
index 51109794d727..7728116e2968 100644
@@ -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
#6 lore raw

PATCH v3 3/5 stddef: Add const_cast()

In reply to Alejandro Colomar

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.

---
 include/linux/stddef.h | 12 ++++++++++++
 1 file changed, 12 insertions(+)
include/linux/stddef.h +12 -0
index e1851c50c89b..d79a81406b78 100644
@@ -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
#7 lore raw

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.

---
 include/linux/stddef.h | 12 ++++++++++++
 1 file changed, 12 insertions(+)
include/linux/stddef.h +1 -0
index e1851c50c89b..d79a81406b78 100644
@@ -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
#8 lore raw

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.

---
 include/linux/stddef.h | 12 ++++++++++++
 1 file changed, 12 insertions(+)
include/linux/stddef.h +0 -0
index e1851c50c89b..d79a81406b78 100644
@@ -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 just de_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>
#9 lore raw

PATCH v3 4/5 string: Add [C]QChar_of() and QVoid_of()

In reply to Alejandro Colomar

These 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
index 12e31c17f0d3..9e59f9697c9e 100644
@@ -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
#10 lore raw

Re: [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
index 3a70ba9ed3021..cb096e9eb9835 100644
@@ -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
#11 lore raw

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 in QVoid_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(), and CQChar_of() helper macros in include/linux/string.h to 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 in QVoid_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 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.

This is a lie, unless anyone can confirm.

[...]

Quoted 3 lines

[Severity: Medium]
Can QChar_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 _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.

You certainly don't want to pass a volatile char* to strchr(3).

Alex

Signature · 1 line
<https://www.alejandro-colomar.es>
#12 lore raw

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
index 9e59f9697c9e..55ca4cb5122c 100644
@@ -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
#13 lore raw

Re: [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
index cb096e9eb9835..3e8a0537b86bb 100644
@@ -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
#14 lore raw

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 in QVoid_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-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.

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
index cb096e9eb9835..3e8a0537b86bb 100644
@@ -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)?

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 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.

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 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.

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]
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))

Quoted 6 lines

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 *?

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>
#15 lore raw

Re: [PATCH v3 5/5] string: Make strprefix() const-qualifier-generic

In reply to Alejandro Colomar

On Tue, 2026-11-10 at 16:33 +0100, Alejandro Colomar wrote:

no commit description on proposed patch

Quoted 11 lines
---
 include/linux/string.h | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
include/linux/string.h +0 -0
index 9e59f9697c9e..55ca4cb5122c 100644

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?

#16 lore raw

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
---
 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
index 9e59f9697c9e..55ca4cb5122c 100644
@@ -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>
#17 lore raw

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
---
 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
index 9e59f9697c9e..55ca4cb5122c 100644
@@ -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

#18 lore raw

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
---
 include/linux/string.h | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
include/linux/string.h +0 -0
index 9e59f9697c9e..55ca4cb5122c 100644

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?

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>
#19 lore raw

Re: [PATCH v3 5/5] string: Make strprefix() const-qualifier-generic

In reply to Alejandro Colomar
Quoted 11 lines · On Tue, 10 Nov 2026 16:33:56 +0100 Alejandro Colomar <[email protected]> wrote:
---
 include/linux/string.h | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
include/linux/string.h +0 -0
index 9e59f9697c9e..55ca4cb5122c 100644

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;
#20 lore raw

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:
#define strprefix(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>