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:
v2:
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().
+
Reported-by: Bartosz Golaszewski
+ Link: https://lore.kernel.org/linux-hardening/20261104-fwnode-for-each-prop-v1-1-6e28fe1c72c4@oss.qualcomm.com/
Cc: Andy Shevchenko
Signed-off-by: Alejandro Colomar
## 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()")
Reported-by: Bartosz Golaszewski
+ Link: https://lore.kernel.org/linux-hardening/20261104-fwnode-for-each-prop-v1-1-6e28fe1c72c4@oss.qualcomm.com/
Cc: Andy Shevchenko
Signed-off-by: Alejandro Colomar
-: ------------ > 3: 2bf5cf7baad8 stddef: Add const_cast()
-: ------------ > 4: 415453a31ebf string: Add [C]QChar_of() and QVoid_of()
-: ------------ > 5: 35bf6672f84f string: Make strprefix() const-qualifier-generic
--
2.55.0