All of lore.kernel.org
 help / color / mirror / Atom feed
From: Alejandro Colomar <alx@kernel.org>
To: linux-hardening@vger.kernel.org
Cc: Krzysztof Kozlowski <krzk@kernel.org>,
	 "Maciej W. Rozycki" <macro@orcam.me.uk>,
	Geert Uytterhoeven <geert@linux-m68k.org>,
	 Andreas Schwab <schwab@linux-m68k.org>,
	Andy Shevchenko <andy@kernel.org>,
	 Bartosz Golaszewski <brgl@kernel.org>,
	Daniel Scally <djrscally@gmail.com>,
	 Danilo Krummrich <dakr@kernel.org>,
	Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	 Hans Verkuil <hverkuil-cisco@xs4all.nl>,
	Heikki Krogerus <heikki.krogerus@linux.intel.com>,
	 Joe Perches <joe@perches.com>, Johan Hovold <johan@kernel.org>,
	Kees Cook <kees@kernel.org>,
	 Laurent Pinchart <laurent.pinchart@ideasonboard.com>,
	Len Brown <lenb@kernel.org>,
	 Linus Torvalds <torvalds@linux-foundation.org>,
	Linus Walleij <linusw@kernel.org>,
	 Namhyung Kim <namhyung@kernel.org>,
	"Rafael J. Wysocki" <rafael@kernel.org>,
	 Rob Herring <robh@kernel.org>,
	Sakari Ailus <sakari.ailus@linux.intel.com>,
	 Saravana Kannan <saravanak@kernel.org>,
	Steven Rostedt <rostedt@goodmis.org>,
	 Tom Zanussi <zanussi@kernel.org>,
	linux-acpi@vger.kernel.org, driver-core@lists.linux.dev,
	 linux-kernel@vger.kernel.org, devicetree@vger.kernel.org,
	linux-gpio@vger.kernel.org
Subject: Re: [PATCH v4 0/6] Add strprefix() and fix a bug with it
Date: Wed, 11 Nov 2026 20:57:30 +0100	[thread overview]
Message-ID: <avTIWshl9klX0obF@devuan> (raw)
In-Reply-To: <cover.1794425425.git.alx@kernel.org>

[-- Attachment #1: Type: text/plain, Size: 12634 bytes --]

> Date: 2026-11-11 20:55:07+0100
> From: Alejandro Colomar <alx@kernel.org>
>
> 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).
> Anyway, as suggested by Steven, I've added a WARN_ON_ONCE() to catch
> such uses, and also added documentation stating this limitation.
> 
> 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 v4
> ~~~~~~~~~~~~~
> 
> -  [1/6]
>    -  Remove deprecation notice.  [Steve]
>    -  Move documentation of str_has_prefix() to strprefix().  [Steve]
>    -  Document str_has_prefix() by comparing it to strprefix().
>    -  Match parameters in documentation and code.
> -  [2/6]
>    -  Add WARN_ON_ONCE() to str_has_prefix().  [Steve]
>    -  Document limitation of str_has_prefix().
> -  [3/6]  -- no changes --
> -  [4/6]
>    -  Document that it might drop const, but doesn't need to.
>    -  Make const_cast() more robust, by replacing 'default:'
>       with 'T:'.  [David]
> -  [5/6]  
>    -  Replace [0] array notation with * pointer notation, to silence
>       spurious -Wpointer-arith diagnostic.  I wouldn't have changed
>       anything if it weren't nontrivial, because that diagnostic is not
>       part of -Wall -Wextra, and is also not too important, but since
>       the solution was trivial, I'm fine with it.  TBH, I prefered [0],
>       but I can live with *.  [sashiko]
> -  [6/6]
>    -  Define the macro after the function, to avoid wrapping the
>       function name in parentheses.  [Joe, David]
>    -  Document why we need CQChar_of() --the simpler typeof()-- is not
>       good enough.  [David]
>    -  Change the function to return non-const.  const_cast() can't add
>       const, it only removes it; thus, we must have it always in the
>       function, and then have const_cast() drop it conditionally.  This
>       also removes a cast.  [sashiko]
> 
> I didn't apply some changes requested by reviewers:
> 
> -  I didn't rename const_cast(), since I'm not convinced about the
>    suggested alternatives being better.  And after all, a cast is only
>    needed for dropping const, but never for adding it (because it adds
>    implicitly), so a const_cast() obviously implies that const might be
>    dropped.
> 
> See range-diff below.
> 
> Testing
> ~~~~~~~
> 
> I haven't changed the implementation of the function meaningfully, so
> haven't re-run tests.  Since the changes since v2 are only about type
> safety and diagnostics, I have built again with 'allmodconfig', just to
> make sure it still compiles.

Ouch!  It seems I had built before adding WARN_ON_ONCE(), not after.
I just saw a compiler error in a background terminal after hitting send.

	$ make 
	  DESCEND objtool
	  CC      arch/x86/purgatory/sha256.o
	In file included from ./include/crypto/sha2.h:10,
			 from lib/crypto/sha256.c:13:
	./include/linux/string.h: In function ‘str_has_prefix’:
	./include/linux/string.h:619:9: error: implicit declaration of function ‘WARN_ON_ONCE’ [-Wimplicit-function-declaration]
	  619 |         WARN_ON_ONCE(len == 0);
	      |         ^~~~~~~~~~~~
	make[4]: *** [arch/x86/purgatory/Makefile:12: arch/x86/purgatory/sha256.o] Error 1
	make[3]: *** [scripts/Makefile.build:572: arch/x86/purgatory] Error 2
	make[2]: *** [scripts/Makefile.build:572: arch/x86] Error 2
	make[1]: *** [/srv/alx/src/linux/linux/master/Makefile:2244: .] Error 2
	make: *** [Makefile:248: __sub-make] Error 2

Will fix that for v5, and fully test again.


Cheers,
Alex

> 
> Alejandro Colomar (6):
>   string: Add strprefix(), and deprecate str_has_prefix()
>   string: Warn if the prefix is the empty string
>   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   | 62 +++++++++++++++++++++++++++++++++++++---
>  lib/tests/string_kunit.c |  9 ++++++
>  4 files changed, 80 insertions(+), 8 deletions(-)
> 
> Range-diff against v3:
> 1:  556766412884 ! 1:  a74fabd985a8 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().
>     +    Move the description from str_has_prefix(), and describe
>     +    str_has_prefix() by comparing it to strprefix().
>      
>          Reported-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
>          Link: https://lore.kernel.org/linux-hardening/20261104-fwnode-for-each-prop-v1-1-6e28fe1c72c4@oss.qualcomm.com/
>     @@ Commit message
>      
>       ## 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
>     -  *
>     -  * 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().
>     ++ *
>     ++ * A common way to test a prefix of a string is to do:
>     ++ *  strncmp(s, 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:
>      + * *  @s+strlen(@prefix) if @s starts with @prefix
>     @@ include/linux/string.h: void memcpy_and_pad(void *dest, size_t dest_len, const v
>      +	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().
>     + /**
>     +  * 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
>     +  *
>     +- * 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().
>     ++ * This is similar to strprefix(), but returns an offset instead of
>     ++ * a pointer.
>        *
>        * Returns:
>        * * strlen(@prefix) if @str starts with @prefix
> -:  ------------ > 2:  cd09427148a0 string: Warn if the prefix is the empty string
> 2:  eed085c85f36 = 3:  0058b60f662c device property: Fix fwnode_name_eq() for empty node names
> 3:  2bf5cf7baad8 ! 4:  ce623fe0432b stddef: Add const_cast()
>     @@ Commit message
>          It's similar to C++'s const_cast(), but it is more restrictive: it can
>          only cast away 'const', and not arbitrary qualifiers.
>      
>     +    David made it a bit more robust.
>     +
>     +    Link: https://lore.kernel.org/linux-hardening/20261111102535.434c2eee@pumpkin/
>     +    Co-developed-by: David Laight <david.laight.linux@gmail.com>
>          Signed-off-by: Alejandro Colomar <alx@kernel.org>
>      
>       ## include/linux/stddef.h ##
>     @@ include/linux/stddef.h: enum {
>       	__TRAILING_OVERLAP(TYPE, NAME, FAM, /* no attrs */, MEMBERS)
>       
>      +/**
>     -+ * const_cast() - Drop const in the left-most derivation of a pointer
>     ++ * const_cast() - At most, 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.
>     ++ * If @p is of type @T, then it 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))
>     ++#define const_cast(T, p)  _Generic(p, const T: (T) (p), T: (p))
>      +
>       #endif
> 4:  415453a31ebf ! 5:  206d2045abcf string: Add [C]QChar_of() and QVoid_of()
>     @@ include/linux/string.h
>      + * 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 QVoid_of(p)       typeof(*(__QVoidptr_of(p)){0})
>      +#define __QVoidptr_of(p)  typeof(1?(p):(void*){0})
>      +
>      +/**
> 5:  35bf6672f84f ! 6:  69d1792e5edf string: Make strprefix() const-qualifier-generic
>     @@ Metadata
>       ## Commit message ##
>          string: Make strprefix() const-qualifier-generic
>      
>     +    Add a macro of the same name, which returns a pointer that is
>     +    const-qualified iff the input string is const-qualified.  The return
>     +    type is a possibly-const-qualified pointer to char, even if the input
>     +    is a possibly-const-qualified pointer to void, which is why we require
>     +    the CQChar_of() macro.
>     +
>     +    The function now needs to return 'const char *', because const_cast()
>     +    can only drop const, but never add it.
>     +
>          Suggested-by: Geert Uytterhoeven <geert@linux-m68k.org>
>     +    Cc: Joe Perches <joe@perches.com>
>     +    Cc: David Laight <david.laight.linux@gmail.com>
>          Signed-off-by: Alejandro Colomar <alx@kernel.org>
>      
>       ## include/linux/string.h ##
>     @@ include/linux/string.h: void memcpy_and_pad(void *dest, size_t dest_len, const v
>        * *  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)
>     ++static inline const char *strprefix(const char *s, const char *prefix)
>       {
>         size_t len = strlen(prefix);
>     -   return strncmp(s, prefix, len) == 0 ? (char *) s + len : NULL;
>     +-  return strncmp(s, prefix, len) == 0 ? (char *) s + len : NULL;
>     ++  return strncmp(s, prefix, len) == 0 ? s + len : NULL;
>     + }
>     ++#define strprefix(s, pfx)  const_cast(CQChar_of(s) *, strprefix(s, pfx))
>     + 
>     + /**
>     +  * str_has_prefix - Test if a string has a given prefix
> 
> -- 
> 2.55.0
> 



-- 
<https://www.alejandro-colomar.es>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

  parent reply	other threads:[~2026-11-11 19:57 UTC|newest]

Thread overview: 65+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-05 11:12 [PATCH v1 0/2] Add strprefix() and fix a bug with it Alejandro Colomar
2026-11-05 11:21 ` Alejandro Colomar
2026-11-05 11:13 ` [PATCH v1 1/2] string: Add strprefix(), and deprecate str_has_prefix() Alejandro Colomar
2026-11-05 11:27   ` Andy Shevchenko
2026-11-05 12:28     ` Alejandro Colomar
2026-11-05 16:14   ` Steven Rostedt
2026-11-05 16:33     ` Alejandro Colomar
2026-11-05 16:50       ` Steven Rostedt
2026-11-05 17:46         ` Alejandro Colomar
2026-11-05 18:08           ` Andy Shevchenko
2026-11-05 18:12             ` Andy Shevchenko
2026-11-05 21:24               ` Alejandro Colomar
2026-11-05 21:48                 ` Linus Torvalds
2026-11-05 22:25                   ` Alejandro Colomar
2026-11-05 23:21                     ` Linus Torvalds
2026-11-06  0:01                       ` Alejandro Colomar
2026-11-06  7:15             ` Krzysztof Kozlowski
2026-11-05 11:13 ` [PATCH v1 2/2] device property: Fix fwnode_name_eq() for empty node names Alejandro Colomar
2026-11-05 11:24 ` [PATCH v1 0/2] Add strprefix() and fix a bug with it Andy Shevchenko
2026-11-05 11:38   ` Alejandro Colomar
2026-11-05 11:59     ` Andy Shevchenko
2026-11-05 12:30 ` Bartosz Golaszewski
2026-11-05 12:49   ` Alejandro Colomar
2026-11-05 12:46 ` [PATCH v2 " Alejandro Colomar
2026-11-05 12:46   ` [PATCH v2 1/2] string: Add strprefix(), and deprecate str_has_prefix() Alejandro Colomar
2026-11-05 12:51     ` Geert Uytterhoeven
2026-11-05 13:12       ` Alejandro Colomar
2026-11-07  2:44         ` Maciej W. Rozycki
2026-11-07  7:33           ` Alejandro Colomar
2026-11-08 11:05             ` Maciej W. Rozycki
2026-11-08 11:49               ` Alejandro Colomar
2026-11-08 11:54                 ` Alejandro Colomar
2026-11-09 22:36                   ` Andy Shevchenko
2026-11-05 12:46   ` [PATCH v2 2/2] device property: Fix fwnode_name_eq() for empty node names Alejandro Colomar
2026-11-05 14:01   ` [PATCH v2 0/2] Add strprefix() and fix a bug with it Bartosz Golaszewski
2026-11-05 14:16     ` Alejandro Colomar
2026-11-05 14:42       ` Andy Shevchenko
2026-11-05 15:06         ` Alejandro Colomar
2026-11-07  2:42         ` Maciej W. Rozycki
2026-11-07 14:05           ` Andy Shevchenko
2026-11-05 15:00       ` Bartosz Golaszewski
2026-11-05 15:14         ` Alejandro Colomar
2026-11-05 15:21       ` Krzysztof Kozlowski
2026-11-05 16:27         ` Steven Rostedt
2026-11-05 16:30           ` Krzysztof Kozlowski
2026-11-05 16:42         ` Alejandro Colomar
2026-11-11 19:55 ` [PATCH v4 0/6] " Alejandro Colomar
2026-11-11 19:55   ` [PATCH v4 1/6] string: Add strprefix(), and deprecate str_has_prefix() Alejandro Colomar
2026-11-11 20:14     ` sashiko-bot
2026-11-12 22:31     ` David Laight
2026-11-12 23:34       ` Alejandro Colomar
2026-11-11 19:55   ` [PATCH v4 2/6] string: Warn if the prefix is the empty string Alejandro Colomar
2026-11-11 20:15     ` sashiko-bot
2026-11-12  1:17     ` kernel test robot
2026-11-12  1:28     ` kernel test robot
2026-11-11 19:55   ` [PATCH v4 3/6] device property: Fix fwnode_name_eq() for empty node names Alejandro Colomar
2026-11-11 19:55   ` [PATCH v4 4/6] stddef: Add const_cast() Alejandro Colomar
2026-11-11 20:11     ` sashiko-bot
2026-11-11 20:18     ` Alejandro Colomar
2026-11-11 19:55   ` [PATCH v4 5/6] string: Add [C]QChar_of() and QVoid_of() Alejandro Colomar
2026-11-11 19:55   ` [PATCH v4 6/6] string: Make strprefix() const-qualifier-generic Alejandro Colomar
2026-11-11 20:11     ` sashiko-bot
2026-11-11 19:57   ` Alejandro Colomar [this message]
2026-11-12 15:17     ` [PATCH v4 0/6] Add strprefix() and fix a bug with it Andy Shevchenko
2026-11-11 20:07   ` Alejandro Colomar

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=avTIWshl9klX0obF@devuan \
    --to=alx@kernel.org \
    --cc=andy@kernel.org \
    --cc=brgl@kernel.org \
    --cc=dakr@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=djrscally@gmail.com \
    --cc=driver-core@lists.linux.dev \
    --cc=geert@linux-m68k.org \
    --cc=gregkh@linuxfoundation.org \
    --cc=heikki.krogerus@linux.intel.com \
    --cc=hverkuil-cisco@xs4all.nl \
    --cc=joe@perches.com \
    --cc=johan@kernel.org \
    --cc=kees@kernel.org \
    --cc=krzk@kernel.org \
    --cc=laurent.pinchart@ideasonboard.com \
    --cc=lenb@kernel.org \
    --cc=linusw@kernel.org \
    --cc=linux-acpi@vger.kernel.org \
    --cc=linux-gpio@vger.kernel.org \
    --cc=linux-hardening@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=macro@orcam.me.uk \
    --cc=namhyung@kernel.org \
    --cc=rafael@kernel.org \
    --cc=robh@kernel.org \
    --cc=rostedt@goodmis.org \
    --cc=sakari.ailus@linux.intel.com \
    --cc=saravanak@kernel.org \
    --cc=schwab@linux-m68k.org \
    --cc=torvalds@linux-foundation.org \
    --cc=zanussi@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.