Linux GPIO subsystem development
 help / color / mirror / Atom feed
* [PATCH v6 00/19] Add strprefix() and fix a bug with it
@ 2026-11-14 15:21 Alejandro Colomar
  2026-11-14 15:21 ` [PATCH v6 01/19] string: Add streq() Alejandro Colomar
                   ` (18 more replies)
  0 siblings, 19 replies; 37+ messages in thread
From: Alejandro Colomar @ 2026-11-14 15:21 UTC (permalink / raw)
  To: linux-hardening
  Cc: Alejandro Colomar, Krzysztof Kozlowski, Maciej W. Rozycki,
	Geert Uytterhoeven, Andreas Schwab, Andy Shevchenko,
	Bartosz Golaszewski, Daniel Scally, Danilo Krummrich,
	Greg Kroah-Hartman, Hans Verkuil, Heikki Krogerus, Joe Perches,
	Johan Hovold, Kees Cook, Laurent Pinchart, Len Brown,
	Linus Torvalds, Linus Walleij, Namhyung Kim, Rafael J. Wysocki,
	Rob Herring, Sakari Ailus, Saravana Kannan, Steven Rostedt,
	Tom Zanussi, linux-acpi, driver-core, linux-kernel, devicetree,
	linux-gpio

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

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(), uses it to replace most uses of
str_has_prefix(), and notably uses strprefix() in one place to fix a bug
reported by Bartosz.

It also adds strsuffix(), for symmetry.  And it also adds streq(3), just
because it simplifies the tests, deduplicates some definitions of it,
and is also a nice interface.

The patch set also removes strstarts() and strends(), since strprefix()
and strsuffix() are drop-in replacements for them (apart from being
useful in more places).

This patch set also adds infrastructure to make string functions return
a pointer that is as qualified as the input pointer.

Changes in v6
~~~~~~~~~~~~~

-  [1/19]  ++ new patch ++
   -  Add streq(3), since it helps simplify some code in the rest of the
      patch set.
-  [2/19]
   -  Update Subject.
   -  Use Closes tag, and remove Andy from Cc.  [Andy]
   -  Add __attribute__((nonnull(1, 2))), for consistency with the
      existing strends().
-  [3/19]
   -  Don't add WARN_ON_ONCE() to str_has_prefix().  It's too
      problematic.  Just document the issue.  [Andy]
   -  Use Closes tag, and remove Andy from Cc.  [Andy]
-  [4/19]
   -  Use Closes tag, and remove Andy from Cc.  [Andy]
-  [5/19]  == no changes ==
-  [6/19]  == no changes ==
-  [7/19]  == no changes ==
-  [8/19]  ++ new patch ++
   -  Prepare for replacing strstarts() with strprefix().  [Andy]
-  [9/19]
   -  Make strstarts() an alias for strprefix(), in preparation for its
      removal.  [Andy]
-  [10/19]  ++ new patch ++
   -  Prepare for replacing strstarts() with strprefix().  [Andy]
-  [11/19]  ++ new patch ++
   -  Tree-wide replacement of strstarts() with strprefix().  [Andy]
-  [12/19]  ++ new patch ++
   -  Remove strstarts().  [Andy]
-  [13/19]  ++ new patch ++
   -  Prepare for replacing str_has_prefix() with strprefix().
-  [14/19]  ++ new patch ++
   -  Replace the trivial uses of str_has_prefix() with strprefix().
-  [15/19]  ++ new patch ++
   -  Replace some simple uses of str_has_prefic() with strprefix().
-  [16/19]  ++ new patch ++
   -  Add strsuffix() for symmetry.  [Andy]
-  [17/19]  ++ new patch ++
   -  Make strends() an alias for strsuffix(), in preparation for its
      removal.  [Andy]
-  [18/19]  ++ new patch ++
   -  Tree-wide replacement of strends() with strsuffix().  [Andy]
-  [19/19]  ++ new patch ++
   -  Remove strends().  [Andy]

See range-diff below.

v1: <https://lore.kernel.org/linux-hardening/cover.1793876837.git.alx@kernel.org/>
v2: <https://lore.kernel.org/linux-hardening/cover.1793882260.git.alx@kernel.org/>
v3: <https://lore.kernel.org/linux-hardening/cover.1794323750.git.alx@kernel.org/>
v4: <https://lore.kernel.org/linux-hardening/cover.1794425425.git.alx@kernel.org/>
v5: <https://lore.kernel.org/linux-hardening/cover.1794574496.git.alx@kernel.org/>

Testing
~~~~~~~

(I've indented the output, to better see the commands.)

$ git log --oneline --graph v7.4-rc1^..strprefix6
	* 8d5cac1ac296 (HEAD -> strprefix, tag: strprefix6) string: Remove strends()
	* 3cad861d3090 treewide: s/\<strends\>/strsuffix/g
	* 8aa3d317d63c string: Make strends() a legacy alias for strsuffix()
	* 636fe759a4eb string: Add strsuffix()
	* 95bfed420dd9 treewide: Replace some uses of str_has_prefix() by strprefix()
	* 0c0070455de0 treewide: Replace boolean uses of str_has_prefix() by strprefix()
	* 024364c695d5 s390/test_unwind: Use bool for booleans
	* cabc673b9ac7 string: Remove strstarts()
	* 782e2941d0f1 treewide: s/strstarts/strprefix/g
	* ec94f69d2925 platform/x86: hp-bioscfg: Rename local variable
	* 212e7fc2cb99 string: Make strstarts() a legacy alias for strprefix()
	* 8a3c50cfe298 perf map: Use bool for return type instead of int
	* 9def9160f777 string: Make strprefix() const-qualifier-generic
	* d9680e101472 string: Add [C]QChar_of() and QVoid_of()
	* 2a26768609bd stddef: Add const_cast()
	* 7fc1321995f1 device property: Fix fwnode_name_eq() for empty node names
	* 5648bb17b88a string: Document corner-case misbehavior in str_has_prefix() for empty prefix
	* 0eaa4ce73a2b string: Add strprefix()
	* cff11fd710fd string: Add streq()
	* 6628e7d4c6b2 (tag: v7.4-rc1, master) Linux 7.4-rc1
$ cp ../kunitconfig .kunitconfig
$ cat .kunitconfig
	CONFIG_KUNIT=y
	CONFIG_STRING_KUNIT_TEST=y
$ mkdir -p ~/tmp/strprefix6
$ export TMPDIR=$HOME/tmp/strprefix6/
$ ./tools/testing/kunit/kunit.py run "string*"
	[15:41:05] Configuring KUnit Kernel ...
	Generating .config ...
	Populating config with:
	$ make ARCH=um O=.kunit olddefconfig
	[15:41:08] Building KUnit Kernel ...
	Populating config with:
	$ make ARCH=um O=.kunit olddefconfig
	Building with:
	$ make all compile_commands.json scripts_gdb ARCH=um O=.kunit --jobs=24
	[15:41:23] Starting KUnit Kernel (1/1)...
	[15:41:23] ============================================================
	Running tests with:
	$ .kunit/linux 'kunit.filter_glob=string*' kunit.enable=1 mem=1G console=tty kunit_shutdown=halt
	[15:41:24] ============= string-stream-test (12 subtests) =============
	[15:41:24] [PASSED] string_stream_managed_init_test
	[15:41:24] [PASSED] string_stream_unmanaged_init_test
	[15:41:24] [PASSED] string_stream_managed_free_test
	[15:41:24] [PASSED] string_stream_resource_free_test
	[15:41:24] [PASSED] string_stream_line_add_test
	[15:41:24] [PASSED] string_stream_variable_length_line_test
	[15:41:24] [PASSED] string_stream_append_test
	[15:41:24] [PASSED] string_stream_append_auto_newline_test
	[15:41:24] [PASSED] string_stream_append_empty_string_test
	[15:41:24] [PASSED] string_stream_no_auto_newline_test
	[15:41:24] [PASSED] string_stream_auto_newline_test
	[15:41:24] [PASSED] string_stream_performance_test
	[15:41:24] =============== [PASSED] string-stream-test ================
	[15:41:24] =================== string (29 subtests) ===================
	[15:41:24] [PASSED] string_test_memset16
	[15:41:24] [PASSED] string_test_memset32
	[15:41:24] [PASSED] string_test_memset64
	[15:41:24] [PASSED] string_test_strlen
	[15:41:24] [PASSED] string_test_strnlen
	[15:41:24] [PASSED] string_test_strchr
	[15:41:24] [PASSED] string_test_strnchr
	[15:41:24] [PASSED] string_test_strrchr
	[15:41:24] [PASSED] string_test_strspn
	[15:41:24] [PASSED] string_test_strprefix
	[15:41:24] [PASSED] string_test_strcmp
	[15:41:24] [PASSED] string_test_strcmp_long_strings
	[15:41:24] [PASSED] string_test_strncmp
	[15:41:24] [PASSED] string_test_strncmp_long_strings
	[15:41:24] [PASSED] string_test_strcasecmp
	[15:41:24] [PASSED] string_test_strcasecmp_long_strings
	[15:41:24] [PASSED] string_test_strncasecmp
	[15:41:24] [PASSED] string_test_strncasecmp_long_strings
	[15:41:24] [PASSED] string_test_strscpy
	[15:41:24] [PASSED] string_test_strcat
	[15:41:24] [PASSED] string_test_strncat
	[15:41:24] [PASSED] string_test_strlcat
	[15:41:24] [PASSED] string_test_strtomem
	[15:41:24] [PASSED] string_test_memtostr
	[15:41:24] [PASSED] string_test_strsuffix
	[15:41:24] [SKIPPED] string_bench_strlen (not enabled)
	[15:41:24] [SKIPPED] string_bench_strnlen (not enabled)
	[15:41:24] [SKIPPED] string_bench_strchr (not enabled)
	[15:41:24] [SKIPPED] string_bench_strrchr (not enabled)
	[15:41:24] ===================== [PASSED] string ======================
	[15:41:24] =============== string_helpers (4 subtests) ================
	[15:41:24] [PASSED] test_get_size
	[15:41:24] [PASSED] test_upper_lower
	[15:41:24] [PASSED] test_unescape
	[15:41:24] [PASSED] test_escape
	[15:41:24] ================= [PASSED] string_helpers ==================
	[15:41:24] ============================================================
	[15:41:24] Testing complete. Ran 45 tests: passed: 41, skipped: 4
	[15:41:24] Elapsed time: 18.895s total, 2.787s configuring, 15.842s building, 0.251s running

$ make allmodconfig
	  HOSTCC  scripts/basic/fixdep
	  HOSTCC  scripts/kconfig/conf.o
	  HOSTCC  scripts/kconfig/confdata.o
	  HOSTCC  scripts/kconfig/expr.o
	  LEX     scripts/kconfig/lexer.lex.c
	  YACC    scripts/kconfig/parser.tab.[ch]
	  HOSTCC  scripts/kconfig/lexer.lex.o
	  HOSTCC  scripts/kconfig/menu.o
	  HOSTCC  scripts/kconfig/parser.tab.o
	  HOSTCC  scripts/kconfig/preprocess.o
	  HOSTCC  scripts/kconfig/symbol.o
	  HOSTCC  scripts/kconfig/util.o
	  HOSTLD  scripts/kconfig/conf
	#
	# configuration written to .config
	#
$ time make -j24 | ts -s
	00:00:00   GEN     arch/x86/include/generated/asm/orc_hash.h
	00:00:00   UPD     arch/x86/include/generated/uapi/asm/bpf_perf_event.h
	00:00:00   UPD     arch/x86/include/generated/uapi/asm/errno.h
...
	00:20:14   LD [M]  net/qrtr/qrtr-tun.ko
	00:20:14   LD [M]  net/qrtr/qrtr-mhi.ko
	00:20:14   LD [M]  virt/lib/irqbypass.ko

	real	20m14.429s
	user	430m41.022s
	sys	24m28.452s


Have a lovely day!
Alex


Alejandro Colomar (19):
  string: Add streq()
  string: Add strprefix()
  string: Document corner-case misbehavior in str_has_prefix() for empty
    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
  perf map: Use bool for return type instead of int
  string: Make strstarts() a legacy alias for strprefix()
  platform/x86: hp-bioscfg: Rename local variable
  treewide: s/strstarts/strprefix/g
  string: Remove strstarts()
  s390/test_unwind: Use bool for booleans
  treewide: Replace boolean uses of str_has_prefix() by strprefix()
  treewide: Replace some uses of str_has_prefix() by strprefix()
  string: Add strsuffix()
  string: Make strends() a legacy alias for strsuffix()
  treewide: s/\<strends\>/strsuffix/g
  string: Remove strends()

 arch/alpha/kernel/setup.c                     |   2 +-
 arch/arm/kernel/module.c                      |  12 +-
 arch/mips/bcm47xx/board.c                     |   4 +-
 arch/mips/bcm63xx/boards/board_bcm963xx.c     |   2 +-
 arch/parisc/kernel/setup.c                    |   2 +-
 arch/powerpc/perf/imc-pmu.c                   |   2 +-
 arch/powerpc/platforms/powernv/pci-ioda.c     |   2 +-
 arch/s390/kernel/early_printk.c               |   2 +-
 arch/s390/kernel/module.c                     |   6 +-
 arch/s390/kernel/setup.c                      |   4 +-
 arch/s390/lib/test_unwind.c                   |  19 +--
 crypto/essiv.c                                |   2 +-
 drivers/base/property.c                       |   5 +-
 drivers/clk/socfpga/clk-gate-a10.c            |   2 -
 drivers/clk/socfpga/clk.h                     |   1 -
 .../firmware/efi/libstub/efi-stub-helper.c    |   2 +-
 drivers/firmware/efi/libstub/gop.c            |  10 +-
 drivers/gpio/gpio-aggregator.c                |   2 +-
 drivers/gpio/gpiolib-shared.c                 |   8 +-
 drivers/gpu/drm/omapdrm/dss/base.c            |   2 +-
 .../gpu/drm/renesas/rcar-du/rcar_du_crtc.c    |   2 +-
 drivers/gpu/drm/xe/xe_configfs.c              |   6 +-
 drivers/hid/hid-uclogic-params.c              |   2 +-
 drivers/iio/pressure/hsc030pa.c               |   2 +-
 drivers/input/mouse/psmouse-base.c            |   2 +-
 drivers/input/mouse/trackpoint.c              |   2 +-
 drivers/md/dm-crypt.c                         |   4 +-
 drivers/md/dm-inlinecrypt.c                   |   2 +-
 drivers/media/i2c/ds90ub960.c                 |   2 +-
 drivers/memory/tegra/tegra20.c                |   4 +-
 drivers/mtd/spi-nor/issi.c                    |   2 +-
 drivers/net/ethernet/amd/pds_core/fw.c        |   5 +-
 drivers/net/ethernet/intel/ice/ice_parser.c   |  10 +-
 drivers/of/property.c                         |   6 +-
 drivers/pci/pwrctrl/core.c                    |   2 +-
 drivers/platform/x86/acerhdf.c                |   6 +-
 drivers/platform/x86/dell/dell-lis3lv02d.c    |   2 +-
 .../x86/hp/hp-bioscfg/spmobj-attributes.c     |  14 +-
 drivers/platform/x86/lenovo/thinkpad_acpi.c   |  54 ++++----
 drivers/platform/x86/touchscreen_dmi.c        |   2 +-
 drivers/remoteproc/imx_dsp_rproc.c            |   2 +-
 drivers/remoteproc/imx_rproc.c                |   8 +-
 drivers/remoteproc/mtk_scp.c                  |   2 +-
 drivers/remoteproc/st_remoteproc.c            |   2 +-
 drivers/remoteproc/stm32_rproc.c              |   2 +-
 drivers/remoteproc/xlnx_r5_remoteproc.c       |   2 +-
 .../media/atomisp/pci/atomisp_csi2_bridge.c   |   2 +-
 drivers/tty/serial/serial_base_bus.c          |   4 +-
 drivers/usb/dwc3/dwc3-pci.c                   |   2 +-
 drivers/usb/musb/musb_cppi41.c                |   4 +-
 drivers/usb/musb/musb_debugfs.c               |  20 +--
 fs/smb/client/reparse.c                       |  14 +-
 include/linux/stddef.h                        |  11 ++
 include/linux/string.h                        | 130 +++++++++++++-----
 init/main.c                                   |   2 +-
 kernel/debug/kdb/kdb_main.c                   |   2 +-
 kernel/kcsan/debugfs.c                        |   2 +-
 kernel/kcsan/report.c                         |   2 +-
 kernel/kprobes.c                              |   4 +-
 kernel/locking/locktorture.c                  |   8 +-
 kernel/module/kallsyms.c                      |   2 +-
 kernel/module/main.c                          |   8 +-
 kernel/power/main.c                           |   2 +-
 kernel/printk/braille.c                       |  14 +-
 kernel/trace/trace.c                          |   9 +-
 kernel/trace/trace_events.c                   |   5 +-
 kernel/trace/trace_events_hist.c              |  58 ++++----
 kernel/trace/trace_events_synth.c             |  10 +-
 kernel/trace/trace_events_user.c              |  22 +--
 kernel/trace/trace_probe.c                    |  22 +--
 kernel/trace/trace_stack.c                    |   6 +-
 lib/tests/string_kunit.c                      |  29 ++--
 mm/arch_numa.c                                |   2 +-
 mm/kfence/report.c                            |  14 +-
 .../hda/codecs/side-codecs/tas2781_hda_i2c.c  |   6 +-
 sound/soc/sdw_utils/soc_sdw_senary_sdca.c     |   2 +-
 tools/perf/util/map.c                         |   2 +-
 77 files changed, 364 insertions(+), 296 deletions(-)

Range-diff against v5:
 -:  ------------ >  1:  cff11fd710fd string: Add streq()
 1:  f25bf43d7f18 !  2:  0eaa4ce73a2b string: Add strprefix(), and deprecate str_has_prefix()
    @@ Metadata
     Author: Alejandro Colomar <alx@kernel.org>
     
      ## Commit message ##
    -    string: Add strprefix(), and deprecate str_has_prefix()
    +    string: Add strprefix()
     
         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
    @@ Commit message
         Move the description from str_has_prefix(), and describe
         str_has_prefix() by comparing it to strprefix().
     
    +    Closes: https://lore.kernel.org/linux-hardening/20261104-fwnode-for-each-prop-v1-1-6e28fe1c72c4@oss.qualcomm.com/
         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/
    -    Cc: Andy Shevchenko <andy@kernel.org>
         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
     + * *  @s + strlen(@prefix) if @s starts with @prefix
     + * *  NULL if @s does not start with @prefix
     + */
    ++__attribute__((nonnull(1, 2)))
     +static inline char *strprefix(const char *s, const char *prefix)
     +{
     +  size_t len = strlen(prefix);
    @@ lib/tests/string_kunit.c: 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_TRUE(test, streq(strprefix("foobar", "foo"), "bar"));
    ++  KUNIT_ASSERT_TRUE(test, streq(strprefix("foobar", ""), "foobar"));
    ++  KUNIT_ASSERT_TRUE(test, streq(strprefix("foobar", "foobar"), ""));
     +  KUNIT_ASSERT_NULL(test, strprefix("foobar", "bar"));
     +}
     +
 2:  df3a35c469c6 !  3:  5648bb17b88a string: Warn if the prefix is the empty string
    @@ Metadata
     Author: Alejandro Colomar <alx@kernel.org>
     
      ## Commit message ##
    -    string: Warn if the prefix is the empty string
    +    string: Document corner-case misbehavior in str_has_prefix() for empty prefix
     
    -    This corner case is mishandled by this function.
    -    Warn if it happens.  Also, document that if such corner case
    -    is needed, strprefix() is preferable.
    -
    -    We need to move str_has_prefix() to "lib/string.h" to be able to use
    -    WARN_ON_ONCE().
    +    For an empty prefix, this function returns a value that can't be
    +    distinguished from a non-matching prefix.  This is wrong, since an empty
    +    prefix is present in every string, and has resulted in bugs in code that
    +    called this function without knowing this issue.  Let's document it.
     
    +    Closes: https://lore.kernel.org/linux-hardening/20261104-fwnode-for-each-prop-v1-1-6e28fe1c72c4@oss.qualcomm.com/
         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/
         Suggested-by: Steven Rostedt <rostedt@goodmis.org>
    -    Cc: Andy Shevchenko <andriy.shevchenko@intel.com>
         Signed-off-by: Alejandro Colomar <alx@kernel.org>
     
      ## include/linux/string.h ##
    -@@ include/linux/string.h: extern char * strstr(const char *, const char *);
    - #ifndef __HAVE_ARCH_STRNSTR
    - extern char * strnstr(const char *, const char *, size_t);
    - #endif
    -+extern size_t str_has_prefix(const char *str, const char *prefix);
    - #ifndef __HAVE_ARCH_STRLEN
    - extern __kernel_size_t strlen(const char *);
    - #endif
     @@ include/linux/string.h: static inline char *strprefix(const char *s, const char *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
    -- *
    -- * This is similar to strprefix(), but returns an offset instead of
    -- * a pointer.
    -- *
    -- * Returns:
    -- * * strlen(@prefix) if @str starts with @prefix
    -- * * 0 if @str does not start with @prefix
    -- */
    --static __always_inline size_t str_has_prefix(const char *str, const char *prefix)
    --{
    --  size_t len = strlen(prefix);
    --  return strncmp(str, prefix, len) == 0 ? len : 0;
    --}
    --
    - /**
    -  * strstarts - does @str start with @prefix?
    -  * @str: string to examine
    -
    - ## lib/string.c ##
    -@@ lib/string.c: char *strnstr(const char *s1, const char *s2, size_t len)
    - EXPORT_SYMBOL(strnstr);
    - #endif
    - 
    -+/**
    -+ * 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
    -+ *
    -+ * This is similar to strprefix(), but returns an offset instead of
    -+ * a pointer.
    -+ *
    +  * This is similar to strprefix(), but returns an offset instead of
    +  * a pointer.
    +  *
     + * Unlike for strprefix(), if the prefix is an empty string, the
     + * behavior is undefined.  If you need to support arbitrary prefixes,
     + * use strprefix().
     + *
    -+ * Returns:
    -+ * * strlen(@prefix) if @str starts with @prefix
    -+ * * 0 if @str does not start with @prefix
    -+ */
    -+size_t str_has_prefix(const char *str, const char *prefix)
    -+{
    -+  size_t len = strlen(prefix);
    -+  WARN_ON_ONCE(len == 0);
    -+  return strncmp(str, prefix, len) == 0 ? len : 0;
    -+}
    -+EXPORT_SYMBOL(str_has_prefix);
    -+
    - #ifndef __HAVE_ARCH_MEMCHR
    - /**
    -  * memchr - Find a character in an area of memory.
    +  * Returns:
    +  * * strlen(@prefix) if @str starts with @prefix
    +  * * 0 if @str does not start with @prefix
 3:  49252e78c9b6 !  4:  7fc1321995f1 device property: Fix fwnode_name_eq() for empty node names
    @@ Commit message
         Let's use strprefix() instead, which also makes this function simpler.
     
         Fixes: a9c8c738066b7 ("device property: Add fwnode_name_eq()")
    +    Closes: https://lore.kernel.org/linux-hardening/20261104-fwnode-for-each-prop-v1-1-6e28fe1c72c4@oss.qualcomm.com/
         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/
    -    Cc: Andy Shevchenko <andy@kernel.org>
         Signed-off-by: Alejandro Colomar <alx@kernel.org>
     
      ## drivers/base/property.c ##
 4:  b615b5afb05e =  5:  2a26768609bd stddef: Add const_cast()
 5:  1294b3d64440 !  6:  d9680e101472 string: Add [C]QChar_of() and QVoid_of()
    @@ include/linux/string.h
     +// 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);
    + /**
    +  * streq - strings equal
    +  *
 6:  0ca0b7aabcfc !  7:  9def9160f777 string: Make strprefix() const-qualifier-generic
    @@ 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,
    -  * *  @s + strlen(@prefix) if @s starts with @prefix
       * *  NULL if @s does not start with @prefix
       */
    + __attribute__((nonnull(1, 2)))
     -static inline char *strprefix(const char *s, const char *prefix)
     +static inline const char *strprefix(const char *s, const char *prefix)
      {
    @@ include/linux/string.h: void memcpy_and_pad(void *dest, size_t dest_len, const v
     +#define strprefix(s, pfx)  const_cast(CQChar_of(s) *, strprefix(s, pfx))
      
      /**
    -  * strstarts - does @str start with @prefix?
    +  * str_has_prefix - Test if a string has a given prefix
 -:  ------------ >  8:  8a3c50cfe298 perf map: Use bool for return type instead of int
 7:  382a44f7c686 !  9:  212e7fc2cb99 string: Implement strstarts() with strprefix()
    @@ Metadata
     Author: Alejandro Colomar <alx@kernel.org>
     
      ## Commit message ##
    -    string: Implement strstarts() with strprefix()
    +    string: Make strstarts() a legacy alias for strprefix()
     
    -    The compiler should be able to optimize out the unused pointer
    -    arithmetic done at the end, so performance-wise this shouldn't change
    -    anything.
    +    strprefix() is drop-in compatible with strstarts() --but see caveats
    +    below--, and provides more functionality for cases where we're
    +    interested in the position where the prefix ends in the string.
     
    -    This change de-duplicates the almost-identical implementations,
    -    and better shows the relationship between the two functions.
    +    strprefix() returns a pointer that can be converted to bool.  This is
    +    converted implicitly in most places, but if the target type is not bool,
    +    then it might be problematic and require an explicit conversion with !!.
    +
    +    I checked that we don't need this anywhere, and also, I expect the
    +    compiler would likely diagnose in such a case, so this should be safe.
     
         Signed-off-by: Alejandro Colomar <alx@kernel.org>
     
      ## include/linux/string.h ##
     @@ include/linux/string.h: static inline const char *strprefix(const char *s, const char *prefix)
    -  */
    - static inline bool strstarts(const char *str, const char *prefix)
    - {
    --  return strncmp(str, prefix, strlen(prefix)) == 0;
    -+  return strprefix(str, prefix);
    +   return strncmp(s, prefix, len) == 0 ? s + len : NULL;
      }
    + #define strprefix(s, pfx)  const_cast(CQChar_of(s) *, strprefix(s, pfx))
    ++#define strstarts(s, pfx)  strprefix(s, pfx)  // legacy alias
      
      /**
    +  * str_has_prefix - Test if a string has a given prefix
    +@@ include/linux/string.h: static __always_inline size_t str_has_prefix(const char *str, const char *prefix
    +   return strncmp(str, prefix, len) == 0 ? len : 0;
    + }
    + 
    +-/**
    +- * strstarts - does @str start with @prefix?
    +- * @str: string to examine
    +- * @prefix: prefix to look for.
    +- *
    +- * Returns:
    +- * True if @str begins with @prefix. False in all other cases.
    +- */
    +-static inline bool strstarts(const char *str, const char *prefix)
    +-{
    +-  return strncmp(str, prefix, strlen(prefix)) == 0;
    +-}
    +-
    + /**
    +  * strends - Check if a string ends with another string.
    +  * @str: NULL-terminated string to check against @suffix
 -:  ------------ > 10:  ec94f69d2925 platform/x86: hp-bioscfg: Rename local variable
 -:  ------------ > 11:  782e2941d0f1 treewide: s/strstarts/strprefix/g
 -:  ------------ > 12:  cabc673b9ac7 string: Remove strstarts()
 -:  ------------ > 13:  024364c695d5 s390/test_unwind: Use bool for booleans
 -:  ------------ > 14:  0c0070455de0 treewide: Replace boolean uses of str_has_prefix() by strprefix()
 -:  ------------ > 15:  95bfed420dd9 treewide: Replace some uses of str_has_prefix() by strprefix()
 -:  ------------ > 16:  636fe759a4eb string: Add strsuffix()
 -:  ------------ > 17:  8aa3d317d63c string: Make strends() a legacy alias for strsuffix()
 -:  ------------ > 18:  3cad861d3090 treewide: s/\<strends\>/strsuffix/g
 -:  ------------ > 19:  8d5cac1ac296 string: Remove strends()



-- 
2.55.0


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

^ permalink raw reply	[flat|nested] 37+ messages in thread

* [PATCH v6 01/19] string: Add streq()
  2026-11-14 15:21 [PATCH v6 00/19] Add strprefix() and fix a bug with it Alejandro Colomar
@ 2026-11-14 15:21 ` Alejandro Colomar
  2026-11-14 15:22 ` [PATCH v6 02/19] string: Add strprefix() Alejandro Colomar
                   ` (17 subsequent siblings)
  18 siblings, 0 replies; 37+ messages in thread
From: Alejandro Colomar @ 2026-11-14 15:21 UTC (permalink / raw)
  To: linux-hardening
  Cc: Alejandro Colomar, Krzysztof Kozlowski, Maciej W. Rozycki,
	Geert Uytterhoeven, Andreas Schwab, Andy Shevchenko,
	Bartosz Golaszewski, Daniel Scally, Danilo Krummrich,
	Greg Kroah-Hartman, Hans Verkuil, Heikki Krogerus, Joe Perches,
	Johan Hovold, Kees Cook, Laurent Pinchart, Len Brown,
	Linus Torvalds, Linus Walleij, Namhyung Kim, Rafael J. Wysocki,
	Rob Herring, Sakari Ailus, Saravana Kannan, Steven Rostedt,
	Tom Zanussi, linux-acpi, driver-core, linux-kernel, devicetree,
	linux-gpio

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

The return value of strcmp(3) being reversed makes it prone to mistakes.
Since most of the time we're only interested in simple equality, add
this API for those cases.

This removes some driver-specific definitions of streq(3).

Signed-off-by: Alejandro Colomar <alx@kernel.org>
---
 drivers/clk/socfpga/clk-gate-a10.c |  2 --
 drivers/clk/socfpga/clk.h          |  1 -
 include/linux/string.h             | 12 ++++++++++++
 3 files changed, 12 insertions(+), 3 deletions(-)

diff --git a/drivers/clk/socfpga/clk-gate-a10.c b/drivers/clk/socfpga/clk-gate-a10.c
index ba12179061b3..df9f660db8a0 100644
--- a/drivers/clk/socfpga/clk-gate-a10.c
+++ b/drivers/clk/socfpga/clk-gate-a10.c
@@ -11,8 +11,6 @@
 
 #include "clk.h"
 
-#define streq(a, b) (strcmp((a), (b)) == 0)
-
 #define to_socfpga_gate_clk(p) container_of(p, struct socfpga_gate_clk, hw.hw)
 
 /* SDMMC Group for System Manager defines */
diff --git a/drivers/clk/socfpga/clk.h b/drivers/clk/socfpga/clk.h
index 9a2fb2dde5b8..2008e6f34a5d 100644
--- a/drivers/clk/socfpga/clk.h
+++ b/drivers/clk/socfpga/clk.h
@@ -19,7 +19,6 @@
 
 #define SOCFPGA_MAX_PARENTS		5
 
-#define streq(a, b) (strcmp((a), (b)) == 0)
 #define SYSMGR_SDMMC_CTRL_SET(smplsel, drvsel) \
 	((((smplsel) & 0x7) << 3) | (((drvsel) & 0x7) << 0))
 
diff --git a/include/linux/string.h b/include/linux/string.h
index 6cb5cdd01158..9b3e578d9aca 100644
--- a/include/linux/string.h
+++ b/include/linux/string.h
@@ -14,6 +14,18 @@
 #include <linux/stdarg.h>
 #include <uapi/linux/string.h>
 
+/**
+ * streq - strings equal
+ *
+ * @s1: string to compare
+ * @s2: string to compare
+ *
+ * Compare two strings for equality.
+ *
+ * Return: true iff the strings are equal
+ */
+#define streq(s1, s2)  (strcmp(s1, s2) == 0)
+
 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);
-- 
2.55.0


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

^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v6 02/19] string: Add strprefix()
  2026-11-14 15:21 [PATCH v6 00/19] Add strprefix() and fix a bug with it Alejandro Colomar
  2026-11-14 15:21 ` [PATCH v6 01/19] string: Add streq() Alejandro Colomar
@ 2026-11-14 15:22 ` Alejandro Colomar
  2026-11-14 15:28   ` Joe Perches
  2026-11-14 15:53   ` Joe Perches
  2026-11-14 15:22 ` [PATCH v6 03/19] string: Document corner-case misbehavior in str_has_prefix() for empty prefix Alejandro Colomar
                   ` (16 subsequent siblings)
  18 siblings, 2 replies; 37+ messages in thread
From: Alejandro Colomar @ 2026-11-14 15:22 UTC (permalink / raw)
  To: linux-hardening
  Cc: Alejandro Colomar, Krzysztof Kozlowski, Maciej W. Rozycki,
	Geert Uytterhoeven, Andreas Schwab, Andy Shevchenko,
	Bartosz Golaszewski, Daniel Scally, Danilo Krummrich,
	Greg Kroah-Hartman, Hans Verkuil, Heikki Krogerus, Joe Perches,
	Johan Hovold, Kees Cook, Laurent Pinchart, Len Brown,
	Linus Torvalds, Linus Walleij, Namhyung Kim, Rafael J. Wysocki,
	Rob Herring, Sakari Ailus, Saravana Kannan, Steven Rostedt,
	Tom Zanussi, linux-acpi, driver-core, linux-kernel, devicetree,
	linux-gpio, Bartosz Golaszewski

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

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

Move the description from str_has_prefix(), and describe
str_has_prefix() by comparing it to strprefix().

Closes: https://lore.kernel.org/linux-hardening/20261104-fwnode-for-each-prop-v1-1-6e28fe1c72c4@oss.qualcomm.com/
Reported-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
---
 include/linux/string.h   | 29 ++++++++++++++++++++++++-----
 lib/tests/string_kunit.c |  9 +++++++++
 2 files changed, 33 insertions(+), 5 deletions(-)

diff --git a/include/linux/string.h b/include/linux/string.h
index 9b3e578d9aca..14e825e902eb 100644
--- a/include/linux/string.h
+++ b/include/linux/string.h
@@ -557,16 +557,35 @@ void memcpy_and_pad(void *dest, size_t dest_len, const void *src, size_t count,
 	       sizeof(*(obj)) - offsetof(typeof(*(obj)), member));	\
 })
 
+/**
+ * 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(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
+ * *  NULL if @s does not start with @prefix
+ */
+__attribute__((nonnull(1, 2)))
+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
  *
- * 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
diff --git a/lib/tests/string_kunit.c b/lib/tests/string_kunit.c
index 0819ace5b027..f603e89866d1 100644
--- a/lib/tests/string_kunit.c
+++ b/lib/tests/string_kunit.c
@@ -287,6 +287,14 @@ static void string_test_strspn(struct kunit *test)
 	}
 }
 
+static void string_test_strprefix(struct kunit *test)
+{
+	KUNIT_ASSERT_TRUE(test, streq(strprefix("foobar", "foo"), "bar"));
+	KUNIT_ASSERT_TRUE(test, streq(strprefix("foobar", ""), "foobar"));
+	KUNIT_ASSERT_TRUE(test, streq(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),
-- 
2.55.0


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

^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v6 03/19] string: Document corner-case misbehavior in str_has_prefix() for empty prefix
  2026-11-14 15:21 [PATCH v6 00/19] Add strprefix() and fix a bug with it Alejandro Colomar
  2026-11-14 15:21 ` [PATCH v6 01/19] string: Add streq() Alejandro Colomar
  2026-11-14 15:22 ` [PATCH v6 02/19] string: Add strprefix() Alejandro Colomar
@ 2026-11-14 15:22 ` Alejandro Colomar
  2026-11-14 15:22 ` [PATCH v6 04/19] device property: Fix fwnode_name_eq() for empty node names Alejandro Colomar
                   ` (15 subsequent siblings)
  18 siblings, 0 replies; 37+ messages in thread
From: Alejandro Colomar @ 2026-11-14 15:22 UTC (permalink / raw)
  To: linux-hardening
  Cc: Alejandro Colomar, Krzysztof Kozlowski, Maciej W. Rozycki,
	Geert Uytterhoeven, Andreas Schwab, Andy Shevchenko,
	Bartosz Golaszewski, Daniel Scally, Danilo Krummrich,
	Greg Kroah-Hartman, Hans Verkuil, Heikki Krogerus, Joe Perches,
	Johan Hovold, Kees Cook, Laurent Pinchart, Len Brown,
	Linus Torvalds, Linus Walleij, Namhyung Kim, Rafael J. Wysocki,
	Rob Herring, Sakari Ailus, Saravana Kannan, Steven Rostedt,
	Tom Zanussi, linux-acpi, driver-core, linux-kernel, devicetree,
	linux-gpio, Bartosz Golaszewski

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

For an empty prefix, this function returns a value that can't be
distinguished from a non-matching prefix.  This is wrong, since an empty
prefix is present in every string, and has resulted in bugs in code that
called this function without knowing this issue.  Let's document it.

Closes: https://lore.kernel.org/linux-hardening/20261104-fwnode-for-each-prop-v1-1-6e28fe1c72c4@oss.qualcomm.com/
Reported-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Suggested-by: Steven Rostedt <rostedt@goodmis.org>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
---
 include/linux/string.h | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/include/linux/string.h b/include/linux/string.h
index 14e825e902eb..58b8b7c02b8d 100644
--- a/include/linux/string.h
+++ b/include/linux/string.h
@@ -587,6 +587,10 @@ static inline char *strprefix(const char *s, const char *prefix)
  * This is similar to strprefix(), but returns an offset instead of
  * a pointer.
  *
+ * Unlike for strprefix(), if the prefix is an empty string, the
+ * behavior is undefined.  If you need to support arbitrary prefixes,
+ * use strprefix().
+ *
  * Returns:
  * * strlen(@prefix) if @str starts with @prefix
  * * 0 if @str does not start with @prefix
-- 
2.55.0


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

^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v6 04/19] device property: Fix fwnode_name_eq() for empty node names
  2026-11-14 15:21 [PATCH v6 00/19] Add strprefix() and fix a bug with it Alejandro Colomar
                   ` (2 preceding siblings ...)
  2026-11-14 15:22 ` [PATCH v6 03/19] string: Document corner-case misbehavior in str_has_prefix() for empty prefix Alejandro Colomar
@ 2026-11-14 15:22 ` Alejandro Colomar
  2026-11-14 15:22 ` [PATCH v6 05/19] stddef: Add const_cast() Alejandro Colomar
                   ` (14 subsequent siblings)
  18 siblings, 0 replies; 37+ messages in thread
From: Alejandro Colomar @ 2026-11-14 15:22 UTC (permalink / raw)
  To: linux-hardening
  Cc: Alejandro Colomar, Krzysztof Kozlowski, Maciej W. Rozycki,
	Geert Uytterhoeven, Andreas Schwab, Andy Shevchenko,
	Bartosz Golaszewski, Daniel Scally, Danilo Krummrich,
	Greg Kroah-Hartman, Hans Verkuil, Heikki Krogerus, Joe Perches,
	Johan Hovold, Kees Cook, Laurent Pinchart, Len Brown,
	Linus Torvalds, Linus Walleij, Namhyung Kim, Rafael J. Wysocki,
	Rob Herring, Sakari Ailus, Saravana Kannan, Steven Rostedt,
	Tom Zanussi, linux-acpi, driver-core, linux-kernel, devicetree,
	linux-gpio, Bartosz Golaszewski

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

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()")
Closes: https://lore.kernel.org/linux-hardening/20261104-fwnode-for-each-prop-v1-1-6e28fe1c72c4@oss.qualcomm.com/
Reported-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
---
 drivers/base/property.c | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/drivers/base/property.c b/drivers/base/property.c
index 51109794d727..7728116e2968 100644
--- a/drivers/base/property.c
+++ b/drivers/base/property.c
@@ -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);
 
-- 
2.55.0


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

^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v6 05/19] stddef: Add const_cast()
  2026-11-14 15:21 [PATCH v6 00/19] Add strprefix() and fix a bug with it Alejandro Colomar
                   ` (3 preceding siblings ...)
  2026-11-14 15:22 ` [PATCH v6 04/19] device property: Fix fwnode_name_eq() for empty node names Alejandro Colomar
@ 2026-11-14 15:22 ` Alejandro Colomar
  2026-11-14 15:22 ` [PATCH v6 06/19] string: Add [C]QChar_of() and QVoid_of() Alejandro Colomar
                   ` (13 subsequent siblings)
  18 siblings, 0 replies; 37+ messages in thread
From: Alejandro Colomar @ 2026-11-14 15:22 UTC (permalink / raw)
  To: linux-hardening
  Cc: Alejandro Colomar, Krzysztof Kozlowski, Maciej W. Rozycki,
	Geert Uytterhoeven, Andreas Schwab, Andy Shevchenko,
	Bartosz Golaszewski, Daniel Scally, Danilo Krummrich,
	Greg Kroah-Hartman, Hans Verkuil, Heikki Krogerus, Joe Perches,
	Johan Hovold, Kees Cook, Laurent Pinchart, Len Brown,
	Linus Torvalds, Linus Walleij, Namhyung Kim, Rafael J. Wysocki,
	Rob Herring, Sakari Ailus, Saravana Kannan, Steven Rostedt,
	Tom Zanussi, linux-acpi, driver-core, linux-kernel, devicetree,
	linux-gpio, David Laight

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

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.

Cc: David Laight <david.laight.linux@gmail.com>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
---
 include/linux/stddef.h | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/include/linux/stddef.h b/include/linux/stddef.h
index e1851c50c89b..26c657465e60 100644
--- a/include/linux/stddef.h
+++ b/include/linux/stddef.h
@@ -194,4 +194,15 @@ enum {
 #define TRAILING_OVERLAP(TYPE, NAME, FAM, MEMBERS)				\
 	__TRAILING_OVERLAP(TYPE, NAME, FAM, /* no attrs */, MEMBERS)
 
+/**
+ * const_cast() - At most, drop const in the left-most derivation of a pointer
+ *
+ * @T: Type for the cast.
+ * @p: Pointer to be casted.
+ *
+ * Cast @p to type @T.  The macro verifies that @p is of type const @T.
+ * This avoids mistakes that change anything other than 'const' in a cast.
+ */
+#define const_cast(T, p)  _Generic(p, const T: (T) (p))
+
 #endif
-- 
2.55.0


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

^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v6 06/19] string: Add [C]QChar_of() and QVoid_of()
  2026-11-14 15:21 [PATCH v6 00/19] Add strprefix() and fix a bug with it Alejandro Colomar
                   ` (4 preceding siblings ...)
  2026-11-14 15:22 ` [PATCH v6 05/19] stddef: Add const_cast() Alejandro Colomar
@ 2026-11-14 15:22 ` Alejandro Colomar
  2026-11-14 15:22 ` [PATCH v6 07/19] string: Make strprefix() const-qualifier-generic Alejandro Colomar
                   ` (12 subsequent siblings)
  18 siblings, 0 replies; 37+ messages in thread
From: Alejandro Colomar @ 2026-11-14 15:22 UTC (permalink / raw)
  To: linux-hardening
  Cc: Alejandro Colomar, Krzysztof Kozlowski, Maciej W. Rozycki,
	Geert Uytterhoeven, Andreas Schwab, Andy Shevchenko,
	Bartosz Golaszewski, Daniel Scally, Danilo Krummrich,
	Greg Kroah-Hartman, Hans Verkuil, Heikki Krogerus, Joe Perches,
	Johan Hovold, Kees Cook, Laurent Pinchart, Len Brown,
	Linus Torvalds, Linus Walleij, Namhyung Kim, Rafael J. Wysocki,
	Rob Herring, Sakari Ailus, Saravana Kannan, Steven Rostedt,
	Tom Zanussi, linux-acpi, driver-core, linux-kernel, devicetree,
	linux-gpio

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

These macros serve to implement qualifier-generic macros as those
specified in ISO C23.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
---
 include/linux/string.h | 30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)

diff --git a/include/linux/string.h b/include/linux/string.h
index 58b8b7c02b8d..ede8883c99a1 100644
--- a/include/linux/string.h
+++ b/include/linux/string.h
@@ -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})
+#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)
+
 /**
  * streq - strings equal
  *
-- 
2.55.0


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

^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v6 07/19] string: Make strprefix() const-qualifier-generic
  2026-11-14 15:21 [PATCH v6 00/19] Add strprefix() and fix a bug with it Alejandro Colomar
                   ` (5 preceding siblings ...)
  2026-11-14 15:22 ` [PATCH v6 06/19] string: Add [C]QChar_of() and QVoid_of() Alejandro Colomar
@ 2026-11-14 15:22 ` Alejandro Colomar
  2026-11-14 15:22 ` [PATCH v6 08/19] perf map: Use bool for return type instead of int Alejandro Colomar
                   ` (11 subsequent siblings)
  18 siblings, 0 replies; 37+ messages in thread
From: Alejandro Colomar @ 2026-11-14 15:22 UTC (permalink / raw)
  To: linux-hardening
  Cc: Alejandro Colomar, Krzysztof Kozlowski, Maciej W. Rozycki,
	Geert Uytterhoeven, Andreas Schwab, Andy Shevchenko,
	Bartosz Golaszewski, Daniel Scally, Danilo Krummrich,
	Greg Kroah-Hartman, Hans Verkuil, Heikki Krogerus, Joe Perches,
	Johan Hovold, Kees Cook, Laurent Pinchart, Len Brown,
	Linus Torvalds, Linus Walleij, Namhyung Kim, Rafael J. Wysocki,
	Rob Herring, Sakari Ailus, Saravana Kannan, Steven Rostedt,
	Tom Zanussi, linux-acpi, driver-core, linux-kernel, devicetree,
	linux-gpio, David Laight

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

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 | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/include/linux/string.h b/include/linux/string.h
index ede8883c99a1..013c6169def0 100644
--- a/include/linux/string.h
+++ b/include/linux/string.h
@@ -603,11 +603,12 @@ void memcpy_and_pad(void *dest, size_t dest_len, const void *src, size_t count,
  * *  NULL if @s does not start with @prefix
  */
 __attribute__((nonnull(1, 2)))
-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 ? 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


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

^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v6 08/19] perf map: Use bool for return type instead of int
  2026-11-14 15:21 [PATCH v6 00/19] Add strprefix() and fix a bug with it Alejandro Colomar
                   ` (6 preceding siblings ...)
  2026-11-14 15:22 ` [PATCH v6 07/19] string: Make strprefix() const-qualifier-generic Alejandro Colomar
@ 2026-11-14 15:22 ` Alejandro Colomar
  2026-11-14 15:22 ` [PATCH v6 09/19] string: Make strstarts() a legacy alias for strprefix() Alejandro Colomar
                   ` (10 subsequent siblings)
  18 siblings, 0 replies; 37+ messages in thread
From: Alejandro Colomar @ 2026-11-14 15:22 UTC (permalink / raw)
  To: linux-hardening
  Cc: Alejandro Colomar, Krzysztof Kozlowski, Maciej W. Rozycki,
	Geert Uytterhoeven, Andreas Schwab, Andy Shevchenko,
	Bartosz Golaszewski, Daniel Scally, Danilo Krummrich,
	Greg Kroah-Hartman, Hans Verkuil, Heikki Krogerus, Joe Perches,
	Johan Hovold, Kees Cook, Laurent Pinchart, Len Brown,
	Linus Torvalds, Linus Walleij, Namhyung Kim, Rafael J. Wysocki,
	Rob Herring, Sakari Ailus, Saravana Kannan, Steven Rostedt,
	Tom Zanussi, linux-acpi, driver-core, linux-kernel, devicetree,
	linux-gpio

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

This is not strictly necessary, but is more robust and readable.
We already use bool in that file, so this improves consistency too.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
---
 tools/perf/util/map.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
index 41cdddc987ee..944f10d32796 100644
--- a/tools/perf/util/map.c
+++ b/tools/perf/util/map.c
@@ -16,7 +16,7 @@
 #include "thread.h"
 #include "vdso.h"
 
-static inline int is_android_lib(const char *filename)
+static inline bool is_android_lib(const char *filename)
 {
 	return strstarts(filename, "/data/app-lib/") ||
 	       strstarts(filename, "/system/lib/");
-- 
2.55.0


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

^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v6 09/19] string: Make strstarts() a legacy alias for strprefix()
  2026-11-14 15:21 [PATCH v6 00/19] Add strprefix() and fix a bug with it Alejandro Colomar
                   ` (7 preceding siblings ...)
  2026-11-14 15:22 ` [PATCH v6 08/19] perf map: Use bool for return type instead of int Alejandro Colomar
@ 2026-11-14 15:22 ` Alejandro Colomar
  2026-11-14 15:33   ` sashiko-bot
  2026-11-14 15:23 ` [PATCH v6 10/19] platform/x86: hp-bioscfg: Rename local variable Alejandro Colomar
                   ` (9 subsequent siblings)
  18 siblings, 1 reply; 37+ messages in thread
From: Alejandro Colomar @ 2026-11-14 15:22 UTC (permalink / raw)
  To: linux-hardening
  Cc: Alejandro Colomar, Krzysztof Kozlowski, Maciej W. Rozycki,
	Geert Uytterhoeven, Andreas Schwab, Andy Shevchenko,
	Bartosz Golaszewski, Daniel Scally, Danilo Krummrich,
	Greg Kroah-Hartman, Hans Verkuil, Heikki Krogerus, Joe Perches,
	Johan Hovold, Kees Cook, Laurent Pinchart, Len Brown,
	Linus Torvalds, Linus Walleij, Namhyung Kim, Rafael J. Wysocki,
	Rob Herring, Sakari Ailus, Saravana Kannan, Steven Rostedt,
	Tom Zanussi, linux-acpi, driver-core, linux-kernel, devicetree,
	linux-gpio

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

strprefix() is drop-in compatible with strstarts() --but see caveats
below--, and provides more functionality for cases where we're
interested in the position where the prefix ends in the string.

strprefix() returns a pointer that can be converted to bool.  This is
converted implicitly in most places, but if the target type is not bool,
then it might be problematic and require an explicit conversion with !!.

I checked that we don't need this anywhere, and also, I expect the
compiler would likely diagnose in such a case, so this should be safe.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
---
 include/linux/string.h | 14 +-------------
 1 file changed, 1 insertion(+), 13 deletions(-)

diff --git a/include/linux/string.h b/include/linux/string.h
index 013c6169def0..0a466360dbb3 100644
--- a/include/linux/string.h
+++ b/include/linux/string.h
@@ -609,6 +609,7 @@ static inline const char *strprefix(const char *s, const char *prefix)
 	return strncmp(s, prefix, len) == 0 ? s + len : NULL;
 }
 #define strprefix(s, pfx)  const_cast(CQChar_of(s) *, strprefix(s, pfx))
+#define strstarts(s, pfx)  strprefix(s, pfx)  // legacy alias
 
 /**
  * str_has_prefix - Test if a string has a given prefix
@@ -632,19 +633,6 @@ static __always_inline size_t str_has_prefix(const char *str, const char *prefix
 	return strncmp(str, prefix, len) == 0 ? len : 0;
 }
 
-/**
- * strstarts - does @str start with @prefix?
- * @str: string to examine
- * @prefix: prefix to look for.
- *
- * Returns:
- * True if @str begins with @prefix. False in all other cases.
- */
-static inline bool strstarts(const char *str, const char *prefix)
-{
-	return strncmp(str, prefix, strlen(prefix)) == 0;
-}
-
 /**
  * strends - Check if a string ends with another string.
  * @str: NULL-terminated string to check against @suffix
-- 
2.55.0


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

^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v6 10/19] platform/x86: hp-bioscfg: Rename local variable
  2026-11-14 15:21 [PATCH v6 00/19] Add strprefix() and fix a bug with it Alejandro Colomar
                   ` (8 preceding siblings ...)
  2026-11-14 15:22 ` [PATCH v6 09/19] string: Make strstarts() a legacy alias for strprefix() Alejandro Colomar
@ 2026-11-14 15:23 ` Alejandro Colomar
  2026-11-14 16:04   ` Joe Perches
  2026-11-14 15:23 ` [PATCH v6 11/19] treewide: s/strstarts/strprefix/g Alejandro Colomar
                   ` (8 subsequent siblings)
  18 siblings, 1 reply; 37+ messages in thread
From: Alejandro Colomar @ 2026-11-14 15:23 UTC (permalink / raw)
  To: linux-hardening
  Cc: Alejandro Colomar, Krzysztof Kozlowski, Maciej W. Rozycki,
	Geert Uytterhoeven, Andreas Schwab, Andy Shevchenko,
	Bartosz Golaszewski, Daniel Scally, Danilo Krummrich,
	Greg Kroah-Hartman, Hans Verkuil, Heikki Krogerus, Joe Perches,
	Johan Hovold, Kees Cook, Laurent Pinchart, Len Brown,
	Linus Torvalds, Linus Walleij, Namhyung Kim, Rafael J. Wysocki,
	Rob Herring, Sakari Ailus, Saravana Kannan, Steven Rostedt,
	Tom Zanussi, linux-acpi, driver-core, linux-kernel, devicetree,
	linux-gpio

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

strprefix shadows a function of the same name.  Let's rename it.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
---
 drivers/platform/x86/hp/hp-bioscfg/spmobj-attributes.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/platform/x86/hp/hp-bioscfg/spmobj-attributes.c b/drivers/platform/x86/hp/hp-bioscfg/spmobj-attributes.c
index 343b2356943e..a38d200bd0d9 100644
--- a/drivers/platform/x86/hp/hp-bioscfg/spmobj-attributes.c
+++ b/drivers/platform/x86/hp/hp-bioscfg/spmobj-attributes.c
@@ -67,7 +67,7 @@ size_t hp_calculate_security_buffer(const char *authentication)
 int hp_populate_security_buffer(u16 *authbuf, const char *authentication)
 {
 	u16 *auth = authbuf;
-	char *strprefix = NULL;
+	char *prefix = NULL;
 	int ret = 0;
 
 	if (strstarts(authentication, BEAM_PREFIX)) {
@@ -87,13 +87,13 @@ int hp_populate_security_buffer(u16 *authbuf, const char *authentication)
 		 */
 
 		/* append UTF_PREFIX to part and then convert it to unicode */
-		strprefix = kasprintf(GFP_KERNEL, "%s%s", UTF_PREFIX,
+		prefix = kasprintf(GFP_KERNEL, "%s%s", UTF_PREFIX,
 				      authentication);
-		if (!strprefix)
+		if (!prefix)
 			return -ENOMEM;
 
-		auth = hp_ascii_to_utf16_unicode(auth, strprefix);
-		kfree(strprefix);
+		auth = hp_ascii_to_utf16_unicode(auth, prefix);
+		kfree(prefix);
 
 		if (!auth) {
 			ret = -EINVAL;
-- 
2.55.0


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

^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v6 11/19] treewide: s/strstarts/strprefix/g
  2026-11-14 15:21 [PATCH v6 00/19] Add strprefix() and fix a bug with it Alejandro Colomar
                   ` (9 preceding siblings ...)
  2026-11-14 15:23 ` [PATCH v6 10/19] platform/x86: hp-bioscfg: Rename local variable Alejandro Colomar
@ 2026-11-14 15:23 ` Alejandro Colomar
  2026-11-14 15:23 ` [PATCH v6 12/19] string: Remove strstarts() Alejandro Colomar
                   ` (7 subsequent siblings)
  18 siblings, 0 replies; 37+ messages in thread
From: Alejandro Colomar @ 2026-11-14 15:23 UTC (permalink / raw)
  To: linux-hardening
  Cc: Alejandro Colomar, Krzysztof Kozlowski, Maciej W. Rozycki,
	Geert Uytterhoeven, Andreas Schwab, Andy Shevchenko,
	Bartosz Golaszewski, Daniel Scally, Danilo Krummrich,
	Greg Kroah-Hartman, Hans Verkuil, Heikki Krogerus, Joe Perches,
	Johan Hovold, Kees Cook, Laurent Pinchart, Len Brown,
	Linus Torvalds, Linus Walleij, Namhyung Kim, Rafael J. Wysocki,
	Rob Herring, Sakari Ailus, Saravana Kannan, Steven Rostedt,
	Tom Zanussi, linux-acpi, driver-core, linux-kernel, devicetree,
	linux-gpio

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

This was a scripted change:

	$ git ls-files \
	| grep '\.[ch]$' \
	| grep -v -e ^tools/ -e ^scripts/ -e ^include/ \
	| xargs grep -l strstarts \
	| xargs sed -i 's/strstarts/strprefix/g';

Signed-off-by: Alejandro Colomar <alx@kernel.org>
---
 arch/arm/kernel/module.c                      | 12 ++---
 arch/mips/bcm47xx/board.c                     |  4 +-
 arch/mips/bcm63xx/boards/board_bcm963xx.c     |  2 +-
 arch/powerpc/perf/imc-pmu.c                   |  2 +-
 crypto/essiv.c                                |  2 +-
 .../firmware/efi/libstub/efi-stub-helper.c    |  2 +-
 drivers/firmware/efi/libstub/gop.c            | 10 ++--
 drivers/gpu/drm/omapdrm/dss/base.c            |  2 +-
 .../gpu/drm/renesas/rcar-du/rcar_du_crtc.c    |  2 +-
 drivers/hid/hid-uclogic-params.c              |  2 +-
 drivers/input/mouse/psmouse-base.c            |  2 +-
 drivers/input/mouse/trackpoint.c              |  2 +-
 drivers/md/dm-crypt.c                         |  4 +-
 drivers/memory/tegra/tegra20.c                |  4 +-
 drivers/mtd/spi-nor/issi.c                    |  2 +-
 drivers/net/ethernet/intel/ice/ice_parser.c   | 10 ++--
 drivers/of/property.c                         |  2 +-
 drivers/pci/pwrctrl/core.c                    |  2 +-
 drivers/platform/x86/acerhdf.c                |  6 +--
 drivers/platform/x86/dell/dell-lis3lv02d.c    |  2 +-
 .../x86/hp/hp-bioscfg/spmobj-attributes.c     |  4 +-
 drivers/platform/x86/lenovo/thinkpad_acpi.c   | 54 +++++++++----------
 drivers/platform/x86/touchscreen_dmi.c        |  2 +-
 drivers/remoteproc/imx_dsp_rproc.c            |  2 +-
 drivers/remoteproc/imx_rproc.c                |  8 +--
 drivers/remoteproc/st_remoteproc.c            |  2 +-
 drivers/remoteproc/stm32_rproc.c              |  2 +-
 drivers/remoteproc/xlnx_r5_remoteproc.c       |  2 +-
 drivers/usb/dwc3/dwc3-pci.c                   |  2 +-
 drivers/usb/musb/musb_cppi41.c                |  4 +-
 drivers/usb/musb/musb_debugfs.c               | 20 +++----
 fs/smb/client/reparse.c                       | 14 ++---
 init/main.c                                   |  2 +-
 kernel/module/kallsyms.c                      |  2 +-
 kernel/module/main.c                          |  8 +--
 .../hda/codecs/side-codecs/tas2781_hda_i2c.c  |  6 +--
 36 files changed, 105 insertions(+), 105 deletions(-)

diff --git a/arch/arm/kernel/module.c b/arch/arm/kernel/module.c
index 55ca3fcd37e8..91af37789ecb 100644
--- a/arch/arm/kernel/module.c
+++ b/arch/arm/kernel/module.c
@@ -22,16 +22,16 @@
 
 bool module_init_section(const char *name)
 {
-	return strstarts(name, ".init") ||
-		strstarts(name, ".ARM.extab.init") ||
-		strstarts(name, ".ARM.exidx.init");
+	return strprefix(name, ".init") ||
+		strprefix(name, ".ARM.extab.init") ||
+		strprefix(name, ".ARM.exidx.init");
 }
 
 bool module_exit_section(const char *name)
 {
-	return strstarts(name, ".exit") ||
-		strstarts(name, ".ARM.extab.exit") ||
-		strstarts(name, ".ARM.exidx.exit");
+	return strprefix(name, ".exit") ||
+		strprefix(name, ".ARM.extab.exit") ||
+		strprefix(name, ".ARM.exidx.exit");
 }
 
 #ifdef CONFIG_ARM_HAS_GROUP_RELOCS
diff --git a/arch/mips/bcm47xx/board.c b/arch/mips/bcm47xx/board.c
index b487f687f62d..25fa1ef36ce8 100644
--- a/arch/mips/bcm47xx/board.c
+++ b/arch/mips/bcm47xx/board.c
@@ -251,7 +251,7 @@ static __init const struct bcm47xx_board_type *bcm47xx_board_get_nvram(void)
 
 	if (bcm47xx_nvram_getenv("hardware_version", buf1, sizeof(buf1)) >= 0) {
 		for (e1 = bcm47xx_board_list_hardware_version; e1->value1; e1++) {
-			if (strstarts(buf1, e1->value1))
+			if (strprefix(buf1, e1->value1))
 				return &e1->board;
 		}
 	}
@@ -259,7 +259,7 @@ static __init const struct bcm47xx_board_type *bcm47xx_board_get_nvram(void)
 	if (bcm47xx_nvram_getenv("hardware_version", buf1, sizeof(buf1)) >= 0 &&
 	    bcm47xx_nvram_getenv("boardnum", buf2, sizeof(buf2)) >= 0) {
 		for (e2 = bcm47xx_board_list_hw_version_num; e2->value1; e2++) {
-			if (!strstarts(buf1, e2->value1) &&
+			if (!strprefix(buf1, e2->value1) &&
 			    !strcmp(buf2, e2->value2))
 				return &e2->board;
 		}
diff --git a/arch/mips/bcm63xx/boards/board_bcm963xx.c b/arch/mips/bcm63xx/boards/board_bcm963xx.c
index 71628dac6c26..8083aeaab5b5 100644
--- a/arch/mips/bcm63xx/boards/board_bcm963xx.c
+++ b/arch/mips/bcm63xx/boards/board_bcm963xx.c
@@ -748,7 +748,7 @@ void __init board_prom_init(void)
 
 	/* dump cfe version */
 	cfe = boot_addr + BCM963XX_CFE_VERSION_OFFSET;
-	if (strstarts(cfe, "cfe-")) {
+	if (strprefix(cfe, "cfe-")) {
 		if(cfe[4] == 'v') {
 			if(cfe[5] == 'd')
 				snprintf(cfe_version, 11, "%s",
diff --git a/arch/powerpc/perf/imc-pmu.c b/arch/powerpc/perf/imc-pmu.c
index a9f8464997a2..aff88b1fd3cf 100644
--- a/arch/powerpc/perf/imc-pmu.c
+++ b/arch/powerpc/perf/imc-pmu.c
@@ -1023,7 +1023,7 @@ static int thread_imc_event_init(struct perf_event *event)
 
 static bool is_thread_imc_pmu(struct perf_event *event)
 {
-	return strstarts(event->pmu->name, "thread_imc");
+	return strprefix(event->pmu->name, "thread_imc");
 }
 
 static __be64 *get_event_base_addr(struct perf_event *event)
diff --git a/crypto/essiv.c b/crypto/essiv.c
index a47a3eab6935..ee1bba6a48ce 100644
--- a/crypto/essiv.c
+++ b/crypto/essiv.c
@@ -508,7 +508,7 @@ static int essiv_create(struct crypto_template *tmpl, struct rtattr **tb)
 			goto out_free_inst;
 		aead_alg = crypto_spawn_aead_alg(&ictx->u.aead_spawn);
 		block_base = &aead_alg->base;
-		if (!strstarts(block_base->cra_name, "authenc(")) {
+		if (!strprefix(block_base->cra_name, "authenc(")) {
 			pr_warn("Only authenc() type AEADs are supported by ESSIV\n");
 			err = -EINVAL;
 			goto out_drop_skcipher;
diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c
index 8e43eb3f418b..fbd0dda856b8 100644
--- a/drivers/firmware/efi/libstub/efi-stub-helper.c
+++ b/drivers/firmware/efi/libstub/efi-stub-helper.c
@@ -104,7 +104,7 @@ efi_status_t efi_parse_options(char const *cmdline)
 			if (parse_option_str(val, "debug"))
 				efi_loglevel = CONSOLE_LOGLEVEL_DEBUG;
 		} else if (!strcmp(param, "video") &&
-			   val && strstarts(val, "efifb:")) {
+			   val && strprefix(val, "efifb:")) {
 			efi_parse_option_graphics(val + strlen("efifb:"));
 		}
 	}
diff --git a/drivers/firmware/efi/libstub/gop.c b/drivers/firmware/efi/libstub/gop.c
index b800a6c2290c..c1cbf2b91867 100644
--- a/drivers/firmware/efi/libstub/gop.c
+++ b/drivers/firmware/efi/libstub/gop.c
@@ -42,7 +42,7 @@ static bool parse_modenum(char *option, char **next)
 {
 	u32 m;
 
-	if (!strstarts(option, "mode="))
+	if (!strprefix(option, "mode="))
 		return false;
 	option += strlen("mode=");
 	m = simple_strtoull(option, &option, 0);
@@ -68,10 +68,10 @@ static bool parse_res(char *option, char **next)
 	h = simple_strtoull(option, &option, 10);
 	if (*option == '-') {
 		option++;
-		if (strstarts(option, "rgb")) {
+		if (strprefix(option, "rgb")) {
 			option += strlen("rgb");
 			pf = PIXEL_RGB_RESERVED_8BIT_PER_COLOR;
-		} else if (strstarts(option, "bgr")) {
+		} else if (strprefix(option, "bgr")) {
 			option += strlen("bgr");
 			pf = PIXEL_BGR_RESERVED_8BIT_PER_COLOR;
 		} else if (isdigit(*option))
@@ -93,7 +93,7 @@ static bool parse_res(char *option, char **next)
 
 static bool parse_auto(char *option, char **next)
 {
-	if (!strstarts(option, "auto"))
+	if (!strprefix(option, "auto"))
 		return false;
 	option += strlen("auto");
 	if (*option && *option++ != ',')
@@ -106,7 +106,7 @@ static bool parse_auto(char *option, char **next)
 
 static bool parse_list(char *option, char **next)
 {
-	if (!strstarts(option, "list"))
+	if (!strprefix(option, "list"))
 		return false;
 	option += strlen("list");
 	if (*option && *option++ != ',')
diff --git a/drivers/gpu/drm/omapdrm/dss/base.c b/drivers/gpu/drm/omapdrm/dss/base.c
index a4ac113e1690..4179a89a4395 100644
--- a/drivers/gpu/drm/omapdrm/dss/base.c
+++ b/drivers/gpu/drm/omapdrm/dss/base.c
@@ -260,7 +260,7 @@ static bool omapdss_component_is_loaded(struct omapdss_comp_node *comp)
 {
 	if (comp->dss_core_component)
 		return true;
-	if (!strstarts(comp->compat, "omapdss,"))
+	if (!strprefix(comp->compat, "omapdss,"))
 		return true;
 	if (omapdss_device_is_registered(comp->node))
 		return true;
diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/renesas/rcar-du/rcar_du_crtc.c
index 59c2a4b76137..60aa8720f622 100644
--- a/drivers/gpu/drm/renesas/rcar-du/rcar_du_crtc.c
+++ b/drivers/gpu/drm/renesas/rcar-du/rcar_du_crtc.c
@@ -1051,7 +1051,7 @@ static int rcar_du_crtc_parse_crc_source(struct rcar_du_crtc *rcrtc,
 	} else if (!strcmp(source_name, "auto")) {
 		*source = VSP1_DU_CRC_OUTPUT;
 		return 0;
-	} else if (strstarts(source_name, "plane")) {
+	} else if (strprefix(source_name, "plane")) {
 		unsigned int i;
 
 		*source = VSP1_DU_CRC_PLANE;
diff --git a/drivers/hid/hid-uclogic-params.c b/drivers/hid/hid-uclogic-params.c
index ed16f50f21ed..fc8297d76a3c 100644
--- a/drivers/hid/hid-uclogic-params.c
+++ b/drivers/hid/hid-uclogic-params.c
@@ -1270,7 +1270,7 @@ static bool uclogic_params_ugee_v2_has_battery(struct hid_device *hdev)
 	    hdev->product == USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L) {
 		struct usb_device *udev = hid_to_usb_dev(hdev);
 
-		if (strstarts(udev->product, "Deco LW"))
+		if (strprefix(udev->product, "Deco LW"))
 			return true;
 	}
 
diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c
index 23164688210d..3f5556227acb 100644
--- a/drivers/input/mouse/psmouse-base.c
+++ b/drivers/input/mouse/psmouse-base.c
@@ -513,7 +513,7 @@ bool psmouse_matches_pnp_id(struct psmouse *psmouse, const char * const ids[])
 	struct serio *serio = psmouse->ps2dev.serio;
 	const char *p = serio->firmware_id;
 
-	if (!strstarts(p, "PNP: "))
+	if (!strprefix(p, "PNP: "))
 		return false;
 
 	p += 5;
diff --git a/drivers/input/mouse/trackpoint.c b/drivers/input/mouse/trackpoint.c
index 3bd8fdf56cd3..b7fc22ffd174 100644
--- a/drivers/input/mouse/trackpoint.c
+++ b/drivers/input/mouse/trackpoint.c
@@ -418,7 +418,7 @@ static bool trackpoint_is_dt_capable(const char *pnp_id)
 		return false;
 
 	/* Must start with "PNP: LEN03" */
-	if (!strstarts(pnp_id, "PNP: LEN03"))
+	if (!strprefix(pnp_id, "PNP: LEN03"))
 		return false;
 
 	/* Ensure enough length before comparing */
diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c
index a7655bba7ce3..e3ce3880f3f1 100644
--- a/drivers/md/dm-crypt.c
+++ b/drivers/md/dm-crypt.c
@@ -2847,7 +2847,7 @@ static int crypt_ctr_auth_cipher(struct crypt_config *cc, char *cipher_api)
 	char *start, *end, *mac_alg = NULL;
 	struct crypto_ahash *mac;
 
-	if (!strstarts(cipher_api, "authenc("))
+	if (!strprefix(cipher_api, "authenc("))
 		return 0;
 
 	start = strchr(cipher_api, '(');
@@ -3051,7 +3051,7 @@ static int crypt_ctr_cipher(struct dm_target *ti, char *cipher_in, char *key)
 		return -ENOMEM;
 	}
 
-	if (strstarts(cipher_in, "capi:"))
+	if (strprefix(cipher_in, "capi:"))
 		ret = crypt_ctr_cipher_new(ti, cipher_in, key, &ivmode, &ivopts);
 	else
 		ret = crypt_ctr_cipher_old(ti, cipher_in, key, &ivmode, &ivopts);
diff --git a/drivers/memory/tegra/tegra20.c b/drivers/memory/tegra/tegra20.c
index 27dd6886f86e..edb4d10ae36d 100644
--- a/drivers/memory/tegra/tegra20.c
+++ b/drivers/memory/tegra/tegra20.c
@@ -408,8 +408,8 @@ tegra20_mc_of_icc_xlate_extended(const struct of_phandle_args *spec, void *data)
 		ndata->node = node;
 
 		/* these clients are isochronous by default */
-		if (strstarts(node->name, "display") ||
-		    strstarts(node->name, "vi"))
+		if (strprefix(node->name, "display") ||
+		    strprefix(node->name, "vi"))
 			ndata->tag = TEGRA_MC_ICC_TAG_ISO;
 		else
 			ndata->tag = TEGRA_MC_ICC_TAG_DEFAULT;
diff --git a/drivers/mtd/spi-nor/issi.c b/drivers/mtd/spi-nor/issi.c
index 2f057d731df2..7fb8fc7b6b8f 100644
--- a/drivers/mtd/spi-nor/issi.c
+++ b/drivers/mtd/spi-nor/issi.c
@@ -138,7 +138,7 @@ static bool issi_pm25lv_match(const struct spi_nor *nor)
 {
 	const char *name = nor->info ? nor->info->name : NULL;
 
-	return name && strstarts(name, "pm25lv");
+	return name && strprefix(name, "pm25lv");
 }
 
 static const struct spi_nor_fixup issi_fixup_list[] = {
diff --git a/drivers/net/ethernet/intel/ice/ice_parser.c b/drivers/net/ethernet/intel/ice/ice_parser.c
index d239102c52ff..0db6407d7453 100644
--- a/drivers/net/ethernet/intel/ice/ice_parser.c
+++ b/drivers/net/ethernet/intel/ice/ice_parser.c
@@ -1375,15 +1375,15 @@ static void ice_parse_lbl_item(struct ice_hw *hw, u16 idx, void *item,
 	lbl_item->idx = lbl_data->idx;
 	memcpy(lbl_item->label, lbl_data->label, sizeof(lbl_item->label));
 
-	if (strstarts(lbl_item->label, ICE_LBL_BST_DVM))
+	if (strprefix(lbl_item->label, ICE_LBL_BST_DVM))
 		lbl_item->type = ICE_LBL_BST_TYPE_DVM;
-	else if (strstarts(lbl_item->label, ICE_LBL_BST_SVM))
+	else if (strprefix(lbl_item->label, ICE_LBL_BST_SVM))
 		lbl_item->type = ICE_LBL_BST_TYPE_SVM;
-	else if (strstarts(lbl_item->label, ICE_LBL_TNL_VXLAN))
+	else if (strprefix(lbl_item->label, ICE_LBL_TNL_VXLAN))
 		lbl_item->type = ICE_LBL_BST_TYPE_VXLAN;
-	else if (strstarts(lbl_item->label, ICE_LBL_TNL_GENEVE))
+	else if (strprefix(lbl_item->label, ICE_LBL_TNL_GENEVE))
 		lbl_item->type = ICE_LBL_BST_TYPE_GENEVE;
-	else if (strstarts(lbl_item->label, ICE_LBL_TNL_UDP_ECPRI))
+	else if (strprefix(lbl_item->label, ICE_LBL_TNL_UDP_ECPRI))
 		lbl_item->type = ICE_LBL_BST_TYPE_UDP_ECPRI;
 
 	if (hw->debug_mask & ICE_DBG_PARSER)
diff --git a/drivers/of/property.c b/drivers/of/property.c
index 45e55f618488..f0729619a2a0 100644
--- a/drivers/of/property.c
+++ b/drivers/of/property.c
@@ -1421,7 +1421,7 @@ DEFINE_SUFFIX_PROP(gpio, "-gpio", "#gpio-cells")
 static struct device_node *parse_pinctrl_n(struct device_node *np,
 					   const char *prop_name, int index)
 {
-	if (!strstarts(prop_name, "pinctrl-"))
+	if (!strprefix(prop_name, "pinctrl-"))
 		return NULL;
 
 	if (!isdigit(prop_name[strlen("pinctrl-")]))
diff --git a/drivers/pci/pwrctrl/core.c b/drivers/pci/pwrctrl/core.c
index 0e2387cf4657..4544352d39c0 100644
--- a/drivers/pci/pwrctrl/core.c
+++ b/drivers/pci/pwrctrl/core.c
@@ -159,7 +159,7 @@ static bool pci_pwrctrl_is_required(struct device_node *np)
 	if (ret < 0)
 		return false;
 
-	if (!strstarts(compat, "pci"))
+	if (!strprefix(compat, "pci"))
 		return false;
 
 	if (of_pci_supply_present(np))
diff --git a/drivers/platform/x86/acerhdf.c b/drivers/platform/x86/acerhdf.c
index 5ce5ad3efe69..25540b169339 100644
--- a/drivers/platform/x86/acerhdf.c
+++ b/drivers/platform/x86/acerhdf.c
@@ -582,9 +582,9 @@ static int __init acerhdf_check_hardware(void)
 		 * check if actual hardware BIOS vendor, product and version
 		 * IDs start with the strings of BIOS table entry
 		 */
-		if (strstarts(vendor, bt->vendor) &&
-		    strstarts(product, bt->product) &&
-		    strstarts(version, bt->version)) {
+		if (strprefix(vendor, bt->vendor) &&
+		    strprefix(product, bt->product) &&
+		    strprefix(version, bt->version)) {
 			found = 1;
 			break;
 		}
diff --git a/drivers/platform/x86/dell/dell-lis3lv02d.c b/drivers/platform/x86/dell/dell-lis3lv02d.c
index fe52bcd896f7..8ff52a7a9242 100644
--- a/drivers/platform/x86/dell/dell-lis3lv02d.c
+++ b/drivers/platform/x86/dell/dell-lis3lv02d.c
@@ -106,7 +106,7 @@ static bool i2c_adapter_is_main_i801(struct i2c_adapter *adap)
 	 * Only match the main I801 adapter and reject secondary adapters
 	 * which names start with "SMBus I801 IDF adapter".
 	 */
-	return strstarts(adap->name, "SMBus I801 adapter");
+	return strprefix(adap->name, "SMBus I801 adapter");
 }
 
 static int find_i801(struct device *dev, void *data)
diff --git a/drivers/platform/x86/hp/hp-bioscfg/spmobj-attributes.c b/drivers/platform/x86/hp/hp-bioscfg/spmobj-attributes.c
index a38d200bd0d9..dd50581a1572 100644
--- a/drivers/platform/x86/hp/hp-bioscfg/spmobj-attributes.c
+++ b/drivers/platform/x86/hp/hp-bioscfg/spmobj-attributes.c
@@ -49,7 +49,7 @@ size_t hp_calculate_security_buffer(const char *authentication)
 
 	authlen = strlen(authentication);
 	size = sizeof(u16) + authlen * sizeof(u16);
-	if (!strstarts(authentication, BEAM_PREFIX))
+	if (!strprefix(authentication, BEAM_PREFIX))
 		size += strlen(UTF_PREFIX) * sizeof(u16);
 
 	return size;
@@ -70,7 +70,7 @@ int hp_populate_security_buffer(u16 *authbuf, const char *authentication)
 	char *prefix = NULL;
 	int ret = 0;
 
-	if (strstarts(authentication, BEAM_PREFIX)) {
+	if (strprefix(authentication, BEAM_PREFIX)) {
 		/*
 		 * BEAM_PREFIX is append to authbuf when a signature
 		 * is provided and Sure Admin is enabled in BIOS
diff --git a/drivers/platform/x86/lenovo/thinkpad_acpi.c b/drivers/platform/x86/lenovo/thinkpad_acpi.c
index 4f81855ad721..293ef2e2c9a5 100644
--- a/drivers/platform/x86/lenovo/thinkpad_acpi.c
+++ b/drivers/platform/x86/lenovo/thinkpad_acpi.c
@@ -1311,9 +1311,9 @@ static int tpacpi_rfk_procfs_write(const enum tpacpi_rfk_id id, char *buf)
 		return -ENODEV;
 
 	while ((cmd = strsep(&buf, ","))) {
-		if (strstarts(cmd, "enable"))
+		if (strprefix(cmd, "enable"))
 			status = TPACPI_RFK_RADIO_ON;
-		else if (strstarts(cmd, "disable"))
+		else if (strprefix(cmd, "disable"))
 			status = TPACPI_RFK_RADIO_OFF;
 		else
 			return -EINVAL;
@@ -4119,12 +4119,12 @@ static int hotkey_write(char *buf)
 
 	res = 0;
 	while ((cmd = strsep(&buf, ","))) {
-		if (strstarts(cmd, "enable")) {
+		if (strprefix(cmd, "enable")) {
 			hotkey_enabledisable_warn(1);
-		} else if (strstarts(cmd, "disable")) {
+		} else if (strprefix(cmd, "disable")) {
 			hotkey_enabledisable_warn(0);
 			res = -EPERM;
-		} else if (strstarts(cmd, "reset")) {
+		} else if (strprefix(cmd, "reset")) {
 			mask = (hotkey_all_mask | hotkey_source_mask)
 				& ~hotkey_reserved_mask;
 		} else if (sscanf(cmd, "0x%x", &mask) == 1) {
@@ -5043,33 +5043,33 @@ static int video_write(char *buf)
 	disable = 0;
 
 	while ((cmd = strsep(&buf, ","))) {
-		if (strstarts(cmd, "lcd_enable")) {
+		if (strprefix(cmd, "lcd_enable")) {
 			enable |= TP_ACPI_VIDEO_S_LCD;
-		} else if (strstarts(cmd, "lcd_disable")) {
+		} else if (strprefix(cmd, "lcd_disable")) {
 			disable |= TP_ACPI_VIDEO_S_LCD;
-		} else if (strstarts(cmd, "crt_enable")) {
+		} else if (strprefix(cmd, "crt_enable")) {
 			enable |= TP_ACPI_VIDEO_S_CRT;
-		} else if (strstarts(cmd, "crt_disable")) {
+		} else if (strprefix(cmd, "crt_disable")) {
 			disable |= TP_ACPI_VIDEO_S_CRT;
 		} else if (video_supported == TPACPI_VIDEO_NEW &&
-			   strstarts(cmd, "dvi_enable")) {
+			   strprefix(cmd, "dvi_enable")) {
 			enable |= TP_ACPI_VIDEO_S_DVI;
 		} else if (video_supported == TPACPI_VIDEO_NEW &&
-			   strstarts(cmd, "dvi_disable")) {
+			   strprefix(cmd, "dvi_disable")) {
 			disable |= TP_ACPI_VIDEO_S_DVI;
-		} else if (strstarts(cmd, "auto_enable")) {
+		} else if (strprefix(cmd, "auto_enable")) {
 			res = video_autosw_set(1);
 			if (res)
 				return res;
-		} else if (strstarts(cmd, "auto_disable")) {
+		} else if (strprefix(cmd, "auto_disable")) {
 			res = video_autosw_set(0);
 			if (res)
 				return res;
-		} else if (strstarts(cmd, "video_switch")) {
+		} else if (strprefix(cmd, "video_switch")) {
 			res = video_outputsw_cycle();
 			if (res)
 				return res;
-		} else if (strstarts(cmd, "expand_toggle")) {
+		} else if (strprefix(cmd, "expand_toggle")) {
 			res = video_expand_toggle();
 			if (res)
 				return res;
@@ -5458,9 +5458,9 @@ static int light_write(char *buf)
 		return -ENODEV;
 
 	while ((cmd = strsep(&buf, ","))) {
-		if (strstarts(cmd, "on")) {
+		if (strprefix(cmd, "on")) {
 			newstatus = 1;
-		} else if (strstarts(cmd, "off")) {
+		} else if (strprefix(cmd, "off")) {
 			newstatus = 0;
 		} else
 			return -EINVAL;
@@ -6983,10 +6983,10 @@ static int brightness_write(char *buf)
 		return level;
 
 	while ((cmd = strsep(&buf, ","))) {
-		if (strstarts(cmd, "up")) {
+		if (strprefix(cmd, "up")) {
 			if (level < bright_maxlvl)
 				level++;
-		} else if (strstarts(cmd, "down")) {
+		} else if (strprefix(cmd, "down")) {
 			if (level > 0)
 				level--;
 		} else if (sscanf(cmd, "level %d", &level) == 1 &&
@@ -7731,13 +7731,13 @@ static int volume_write(char *buf)
 
 	while ((cmd = strsep(&buf, ","))) {
 		if (!tp_features.mixer_no_level_control) {
-			if (strstarts(cmd, "up")) {
+			if (strprefix(cmd, "up")) {
 				if (new_mute)
 					new_mute = 0;
 				else if (new_level < TP_EC_VOLUME_MAX)
 					new_level++;
 				continue;
-			} else if (strstarts(cmd, "down")) {
+			} else if (strprefix(cmd, "down")) {
 				if (new_mute)
 					new_mute = 0;
 				else if (new_level > 0)
@@ -7749,9 +7749,9 @@ static int volume_write(char *buf)
 				continue;
 			}
 		}
-		if (strstarts(cmd, "mute"))
+		if (strprefix(cmd, "mute"))
 			new_mute = TP_EC_AUDIO_MUTESW_MSK;
-		else if (strstarts(cmd, "unmute"))
+		else if (strprefix(cmd, "unmute"))
 			new_mute = 0;
 		else
 			return -EINVAL;
@@ -9245,9 +9245,9 @@ static int fan_write_cmd_level(const char *cmd, int *rc)
 {
 	int level;
 
-	if (strstarts(cmd, "level auto"))
+	if (strprefix(cmd, "level auto"))
 		level = TP_EC_FAN_AUTO;
-	else if (strstarts(cmd, "level disengaged") || strstarts(cmd, "level full-speed"))
+	else if (strprefix(cmd, "level disengaged") || strprefix(cmd, "level full-speed"))
 		level = TP_EC_FAN_FULLSPEED;
 	else if (sscanf(cmd, "level %d", &level) != 1)
 		return 0;
@@ -9265,7 +9265,7 @@ static int fan_write_cmd_level(const char *cmd, int *rc)
 
 static int fan_write_cmd_enable(const char *cmd, int *rc)
 {
-	if (!strstarts(cmd, "enable"))
+	if (!strprefix(cmd, "enable"))
 		return 0;
 
 	*rc = fan_set_enable();
@@ -9280,7 +9280,7 @@ static int fan_write_cmd_enable(const char *cmd, int *rc)
 
 static int fan_write_cmd_disable(const char *cmd, int *rc)
 {
-	if (!strstarts(cmd, "disable"))
+	if (!strprefix(cmd, "disable"))
 		return 0;
 
 	*rc = fan_set_disable();
diff --git a/drivers/platform/x86/touchscreen_dmi.c b/drivers/platform/x86/touchscreen_dmi.c
index d83c387821ea..72f18dfc4368 100644
--- a/drivers/platform/x86/touchscreen_dmi.c
+++ b/drivers/platform/x86/touchscreen_dmi.c
@@ -1877,7 +1877,7 @@ static void ts_dmi_add_props(struct i2c_client *client)
 	int error;
 
 	if (has_acpi_companion(dev) &&
-	    strstarts(client->name, ts_data->acpi_name)) {
+	    strprefix(client->name, ts_data->acpi_name)) {
 		error = device_create_managed_software_node(dev, ts_data->properties, NULL);
 		if (error)
 			dev_err(dev, "failed to add properties: %d\n", error);
diff --git a/drivers/remoteproc/imx_dsp_rproc.c b/drivers/remoteproc/imx_dsp_rproc.c
index 96d619daf3f5..8e8085f27a64 100644
--- a/drivers/remoteproc/imx_dsp_rproc.c
+++ b/drivers/remoteproc/imx_dsp_rproc.c
@@ -699,7 +699,7 @@ static int imx_dsp_rproc_add_carveout(struct imx_dsp_rproc *priv)
 		 * Ignore the first memory region which will be used vdev buffer.
 		 * No need to do extra handlings, rproc_add_virtio_dev will handle it.
 		 */
-		if (strstarts(res.name, "vdev0buffer"))
+		if (strprefix(res.name, "vdev0buffer"))
 			continue;
 
 		if (imx_dsp_rproc_sys_to_da(priv, res.start, resource_size(&res), &da))
diff --git a/drivers/remoteproc/imx_rproc.c b/drivers/remoteproc/imx_rproc.c
index 581bb07f5893..9f39e1a10f99 100644
--- a/drivers/remoteproc/imx_rproc.c
+++ b/drivers/remoteproc/imx_rproc.c
@@ -656,10 +656,10 @@ static int imx_rproc_prepare(struct rproc *rproc)
 		 * Ignore the first memory region which will be used vdev buffer.
 		 * No need to do extra handlings, rproc_add_virtio_dev will handle it.
 		 */
-		if (strstarts(res.name, "vdev0buffer"))
+		if (strprefix(res.name, "vdev0buffer"))
 			continue;
 
-		if (strstarts(res.name, "rsc-table"))
+		if (strprefix(res.name, "rsc-table"))
 			continue;
 
 		/* No need to translate pa to da, i.MX use same map */
@@ -834,7 +834,7 @@ static int imx_rproc_addr_init(struct imx_rproc *priv,
 		}
 
 		/* Not map vdevbuffer, vdevring region */
-		if (strstarts(res.name, "vdev"))
+		if (strprefix(res.name, "vdev"))
 			continue;
 
 		if (b >= IMX_RPROC_MEM_MAX)
@@ -848,7 +848,7 @@ static int imx_rproc_addr_init(struct imx_rproc *priv,
 		}
 		priv->mem[b].sys_addr = res.start;
 		priv->mem[b].size = resource_size(&res);
-		if (strstarts(res.name, "rsc-table"))
+		if (strprefix(res.name, "rsc-table"))
 			priv->rsc_table = priv->mem[b].cpu_addr;
 		b++;
 	}
diff --git a/drivers/remoteproc/st_remoteproc.c b/drivers/remoteproc/st_remoteproc.c
index 486180cdccb4..3a351313b0c1 100644
--- a/drivers/remoteproc/st_remoteproc.c
+++ b/drivers/remoteproc/st_remoteproc.c
@@ -106,7 +106,7 @@ static int st_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw)
 			return ret;
 
 		/*  No need to map vdev buffer */
-		if (!strstarts(res.name, "vdev0buffer")) {
+		if (!strprefix(res.name, "vdev0buffer")) {
 			/* Register memory region */
 			mem = rproc_mem_entry_init(dev, NULL,
 						   (dma_addr_t)res.start,
diff --git a/drivers/remoteproc/stm32_rproc.c b/drivers/remoteproc/stm32_rproc.c
index a47e040c6db8..254ebc820721 100644
--- a/drivers/remoteproc/stm32_rproc.c
+++ b/drivers/remoteproc/stm32_rproc.c
@@ -203,7 +203,7 @@ static int stm32_rproc_prepare(struct rproc *rproc)
 		}
 
 		/*  No need to map vdev buffer */
-		if (!strstarts(res.name, "vdev0buffer")) {
+		if (!strprefix(res.name, "vdev0buffer")) {
 			/* Register memory region */
 			mem = rproc_mem_entry_init(dev, NULL,
 						   (dma_addr_t)res.start,
diff --git a/drivers/remoteproc/xlnx_r5_remoteproc.c b/drivers/remoteproc/xlnx_r5_remoteproc.c
index a9873a37dd8f..6b2eaa221334 100644
--- a/drivers/remoteproc/xlnx_r5_remoteproc.c
+++ b/drivers/remoteproc/xlnx_r5_remoteproc.c
@@ -471,7 +471,7 @@ static int add_mem_regions_carveout(struct rproc *rproc)
 		if (err)
 			return 0;
 
-		if (strstarts(res.name, "vdev0buffer")) {
+		if (strprefix(res.name, "vdev0buffer")) {
 			/* Init reserved memory for vdev buffer */
 			rproc_mem = rproc_of_resm_mem_entry_init(&rproc->dev, i,
 								 resource_size(&res),
diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c
index 6c1cbb722ca8..21e6e14ced31 100644
--- a/drivers/usb/dwc3/dwc3-pci.c
+++ b/drivers/usb/dwc3/dwc3-pci.c
@@ -301,7 +301,7 @@ static int dwc3_pci_quirks(struct dwc3_pci *dwc,
 			    acpi_quirk_skip_acpi_ac_and_battery() &&
 			    /* Lenovo Yoga Tablet 2 Pro 1380 uses LC824206XA instead */
 			    !(bios_ver &&
-			      strstarts(bios_ver, "BLADE_21.X64.0005.R00.1504101516"))) {
+			      strprefix(bios_ver, "BLADE_21.X64.0005.R00.1504101516"))) {
 				dev_info(&pdev->dev, "Using TUSB1211 phy for charger detection\n");
 				swnode = &dwc3_pci_intel_phy_charger_detect_swnode;
 			}
diff --git a/drivers/usb/musb/musb_cppi41.c b/drivers/usb/musb/musb_cppi41.c
index 28e958fcc862..c0786ad5a97f 100644
--- a/drivers/usb/musb/musb_cppi41.c
+++ b/drivers/usb/musb/musb_cppi41.c
@@ -686,9 +686,9 @@ static int cppi41_dma_controller_start(struct cppi41_dma_controller *controller)
 		ret = of_property_read_string_index(np, "dma-names", i, &str);
 		if (ret)
 			goto err;
-		if (strstarts(str, "tx"))
+		if (strprefix(str, "tx"))
 			is_tx = 1;
-		else if (strstarts(str, "rx"))
+		else if (strprefix(str, "rx"))
 			is_tx = 0;
 		else {
 			dev_err(dev, "Wrong dmatype %s\n", str);
diff --git a/drivers/usb/musb/musb_debugfs.c b/drivers/usb/musb/musb_debugfs.c
index 5092d62c2062..97cf89c6e451 100644
--- a/drivers/usb/musb/musb_debugfs.c
+++ b/drivers/usb/musb/musb_debugfs.c
@@ -179,36 +179,36 @@ static ssize_t musb_test_mode_write(struct file *file,
 		goto ret;
 	}
 
-	if (strstarts(buf, "force host full-speed"))
+	if (strprefix(buf, "force host full-speed"))
 		test = MUSB_TEST_FORCE_HOST | MUSB_TEST_FORCE_FS;
 
-	else if (strstarts(buf, "force host high-speed"))
+	else if (strprefix(buf, "force host high-speed"))
 		test = MUSB_TEST_FORCE_HOST | MUSB_TEST_FORCE_HS;
 
-	else if (strstarts(buf, "force host"))
+	else if (strprefix(buf, "force host"))
 		test = MUSB_TEST_FORCE_HOST;
 
-	else if (strstarts(buf, "fifo access"))
+	else if (strprefix(buf, "fifo access"))
 		test = MUSB_TEST_FIFO_ACCESS;
 
-	else if (strstarts(buf, "force full-speed"))
+	else if (strprefix(buf, "force full-speed"))
 		test = MUSB_TEST_FORCE_FS;
 
-	else if (strstarts(buf, "force high-speed"))
+	else if (strprefix(buf, "force high-speed"))
 		test = MUSB_TEST_FORCE_HS;
 
-	else if (strstarts(buf, "test packet")) {
+	else if (strprefix(buf, "test packet")) {
 		test = MUSB_TEST_PACKET;
 		musb_load_testpacket(musb);
 	}
 
-	else if (strstarts(buf, "test K"))
+	else if (strprefix(buf, "test K"))
 		test = MUSB_TEST_K;
 
-	else if (strstarts(buf, "test J"))
+	else if (strprefix(buf, "test J"))
 		test = MUSB_TEST_J;
 
-	else if (strstarts(buf, "test SE0 NAK"))
+	else if (strprefix(buf, "test SE0 NAK"))
 		test = MUSB_TEST_SE0_NAK;
 
 	musb_writeb(musb->mregs, MUSB_TESTMODE, test);
diff --git a/fs/smb/client/reparse.c b/fs/smb/client/reparse.c
index 3a27773186ae..d35af97cdbad 100644
--- a/fs/smb/client/reparse.c
+++ b/fs/smb/client/reparse.c
@@ -97,7 +97,7 @@ static int create_native_symlink(const unsigned int xid, struct inode *inode,
 		 * ensure compatibility of this symlink stored in absolute form
 		 * on the SMB server.
 		 */
-		if (!strstarts(symname, symroot)) {
+		if (!strprefix(symname, symroot)) {
 			/*
 			 * If the absolute Linux symlink target path is not
 			 * inside "symlinkroot" location then there is no way
@@ -171,7 +171,7 @@ static int create_native_symlink(const unsigned int xid, struct inode *inode,
 	 * letter so the prefix occupies six characters in UTF-16 too.
 	 */
 	if (!(sbflags & CIFS_MOUNT_POSIX_PATHS) && symname[0] == '/') {
-		if (!strstarts(sym, "\\??\\") || !isascii(sym[4]) ||
+		if (!strprefix(sym, "\\??\\") || !isascii(sym[4]) ||
 		    !isalpha(sym[4]) || sym[5] != ':') {
 			rc = -EINVAL;
 			goto out;
@@ -905,11 +905,11 @@ int smb2_parse_native_symlink(char **target, const char *buf, unsigned int len,
 		 */
 		abs_path = smb_target;
 globalroot:
-		if (strstarts(abs_path, "\\??\\"))
+		if (strprefix(abs_path, "\\??\\"))
 			abs_path += sizeof("\\??\\")-1;
-		else if (strstarts(abs_path, "\\DosDevices\\"))
+		else if (strprefix(abs_path, "\\DosDevices\\"))
 			abs_path += sizeof("\\DosDevices\\")-1;
-		else if (strstarts(abs_path, "\\GLOBAL??\\"))
+		else if (strprefix(abs_path, "\\GLOBAL??\\"))
 			abs_path += sizeof("\\GLOBAL??\\")-1;
 		else
 			goto out_unhandled_target;
@@ -918,10 +918,10 @@ int smb2_parse_native_symlink(char **target, const char *buf, unsigned int len,
 		if (abs_path[0] == '\\')
 			abs_path++;
 
-		while (strstarts(abs_path, "Global\\"))
+		while (strprefix(abs_path, "Global\\"))
 			abs_path += sizeof("Global\\")-1;
 
-		if (strstarts(abs_path, "GLOBALROOT\\")) {
+		if (strprefix(abs_path, "GLOBALROOT\\")) {
 			/* Label globalroot requires path with leading '\\', so do not trim '\\' */
 			abs_path += sizeof("GLOBALROOT")-1;
 			goto globalroot;
diff --git a/init/main.c b/init/main.c
index 7b0c8c34e6a9..44513443d127 100644
--- a/init/main.c
+++ b/init/main.c
@@ -586,7 +586,7 @@ static int __init unknown_bootoption(char *param, char *val,
 
 	/* Handle bootloader identifier */
 	for (int i = 0; bootloader[i]; i++) {
-		if (strstarts(param, bootloader[i]))
+		if (strprefix(param, bootloader[i]))
 			return 0;
 	}
 
diff --git a/kernel/module/kallsyms.c b/kernel/module/kallsyms.c
index 1ff8ce857d92..bcdf2ebe0d32 100644
--- a/kernel/module/kallsyms.c
+++ b/kernel/module/kallsyms.c
@@ -68,7 +68,7 @@ static char elf_type(const Elf_Sym *sym, const struct load_info *info)
 		else
 			return 'b';
 	}
-	if (strstarts(info->secstrings + sechdrs[sym->st_shndx].sh_name,
+	if (strprefix(info->secstrings + sechdrs[sym->st_shndx].sh_name,
 		      ".debug")) {
 		return 'n';
 	}
diff --git a/kernel/module/main.c b/kernel/module/main.c
index 34e0204ea37c..d97502225b3d 100644
--- a/kernel/module/main.c
+++ b/kernel/module/main.c
@@ -1195,7 +1195,7 @@ static bool verify_module_namespace(const char *namespace, const char *modname)
 	const char *sep;
 	bool glob;
 
-	if (!strstarts(namespace, prefix))
+	if (!strprefix(namespace, prefix))
 		return false;
 
 	for (namespace += strlen(prefix); *namespace; namespace = sep) {
@@ -1836,7 +1836,7 @@ static int setup_modinfo(struct module *mod, struct load_info *info)
 		 * 'module:' prefixed namespaces are implicit, disallow
 		 * explicit imports.
 		 */
-		if (strstarts(imported_namespace, "module:")) {
+		if (strprefix(imported_namespace, "module:")) {
 			pr_err("%s: module tries to import module namespace: %s\n",
 			       mod->name, imported_namespace);
 			return -EPERM;
@@ -1863,12 +1863,12 @@ static void free_modinfo(struct module *mod)
 
 bool __weak module_init_section(const char *name)
 {
-	return strstarts(name, ".init");
+	return strprefix(name, ".init");
 }
 
 bool __weak module_exit_section(const char *name)
 {
-	return strstarts(name, ".exit");
+	return strprefix(name, ".exit");
 }
 
 static int validate_section_offset(const struct load_info *info, Elf_Shdr *shdr)
diff --git a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c
index 0c8faa583baa..bf24dce50e23 100644
--- a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c
+++ b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c
@@ -636,14 +636,14 @@ static int tas2781_hda_i2c_probe(struct i2c_client *clt)
 		tas_hda->priv->chip_id = TAS2781;
 		hda_priv->save_calibration = tas2781_save_calibration;
 		tas_hda->priv->global_addr = TAS2781_GLOBAL_ADDR;
-	} else if (strstarts(dev_name(&clt->dev), "i2c-TXNW2770")) {
+	} else if (strprefix(dev_name(&clt->dev), "i2c-TXNW2770")) {
 		/*
 		 * TAS2770, has no on-chip DSP, so no calibration data
 		 * required; has no global I2C address supported.
 		 */
 		device_name = "TXNW2770";
 		hda_priv->hda_chip_id = HDA_TAS2770;
-	} else if (strstarts(dev_name(&clt->dev),
+	} else if (strprefix(dev_name(&clt->dev),
 			     "i2c-TXNW2781:00-tas2781-hda.0")) {
 		device_name = "TXNW2781";
 		hda_priv->hda_chip_id = HDA_TAS2781;
@@ -659,7 +659,7 @@ static int tas2781_hda_i2c_probe(struct i2c_client *clt)
 		hda_priv->hda_chip_id = HDA_TAS2563;
 		hda_priv->save_calibration = tas2563_save_calibration;
 		tas_hda->priv->global_addr = TAS2563_GLOBAL_ADDR;
-	} else if (strstarts(dev_name(&clt->dev), "i2c-TXNW5825")) {
+	} else if (strprefix(dev_name(&clt->dev), "i2c-TXNW5825")) {
 		/*
 		 * TAS5825, integrated on-chip DSP without
 		 * global I2C address and calibration supported.
-- 
2.55.0


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

^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v6 12/19] string: Remove strstarts()
  2026-11-14 15:21 [PATCH v6 00/19] Add strprefix() and fix a bug with it Alejandro Colomar
                   ` (10 preceding siblings ...)
  2026-11-14 15:23 ` [PATCH v6 11/19] treewide: s/strstarts/strprefix/g Alejandro Colomar
@ 2026-11-14 15:23 ` Alejandro Colomar
  2026-11-14 15:29   ` sashiko-bot
  2026-11-14 15:23 ` [PATCH v6 13/19] s390/test_unwind: Use bool for booleans Alejandro Colomar
                   ` (6 subsequent siblings)
  18 siblings, 1 reply; 37+ messages in thread
From: Alejandro Colomar @ 2026-11-14 15:23 UTC (permalink / raw)
  To: linux-hardening
  Cc: Alejandro Colomar, Krzysztof Kozlowski, Maciej W. Rozycki,
	Geert Uytterhoeven, Andreas Schwab, Andy Shevchenko,
	Bartosz Golaszewski, Daniel Scally, Danilo Krummrich,
	Greg Kroah-Hartman, Hans Verkuil, Heikki Krogerus, Joe Perches,
	Johan Hovold, Kees Cook, Laurent Pinchart, Len Brown,
	Linus Torvalds, Linus Walleij, Namhyung Kim, Rafael J. Wysocki,
	Rob Herring, Sakari Ailus, Saravana Kannan, Steven Rostedt,
	Tom Zanussi, linux-acpi, driver-core, linux-kernel, devicetree,
	linux-gpio

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

It's now unused, as it's been fully replaced by strprefix().

Signed-off-by: Alejandro Colomar <alx@kernel.org>
---
 include/linux/string.h | 1 -
 1 file changed, 1 deletion(-)

diff --git a/include/linux/string.h b/include/linux/string.h
index 0a466360dbb3..6b0de932e23a 100644
--- a/include/linux/string.h
+++ b/include/linux/string.h
@@ -609,7 +609,6 @@ static inline const char *strprefix(const char *s, const char *prefix)
 	return strncmp(s, prefix, len) == 0 ? s + len : NULL;
 }
 #define strprefix(s, pfx)  const_cast(CQChar_of(s) *, strprefix(s, pfx))
-#define strstarts(s, pfx)  strprefix(s, pfx)  // legacy alias
 
 /**
  * str_has_prefix - Test if a string has a given prefix
-- 
2.55.0


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

^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v6 13/19] s390/test_unwind: Use bool for booleans
  2026-11-14 15:21 [PATCH v6 00/19] Add strprefix() and fix a bug with it Alejandro Colomar
                   ` (11 preceding siblings ...)
  2026-11-14 15:23 ` [PATCH v6 12/19] string: Remove strstarts() Alejandro Colomar
@ 2026-11-14 15:23 ` Alejandro Colomar
  2026-11-14 15:23 ` [PATCH v6 14/19] treewide: Replace boolean uses of str_has_prefix() by strprefix() Alejandro Colomar
                   ` (5 subsequent siblings)
  18 siblings, 0 replies; 37+ messages in thread
From: Alejandro Colomar @ 2026-11-14 15:23 UTC (permalink / raw)
  To: linux-hardening
  Cc: Alejandro Colomar, Krzysztof Kozlowski, Maciej W. Rozycki,
	Geert Uytterhoeven, Andreas Schwab, Andy Shevchenko,
	Bartosz Golaszewski, Daniel Scally, Danilo Krummrich,
	Greg Kroah-Hartman, Hans Verkuil, Heikki Krogerus, Joe Perches,
	Johan Hovold, Kees Cook, Laurent Pinchart, Len Brown,
	Linus Torvalds, Linus Walleij, Namhyung Kim, Rafael J. Wysocki,
	Rob Herring, Sakari Ailus, Saravana Kannan, Steven Rostedt,
	Tom Zanussi, linux-acpi, driver-core, linux-kernel, devicetree,
	linux-gpio

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

This is obviously good per se, but it will also allow using strprefix()
instead of str_has_prefix().

Signed-off-by: Alejandro Colomar <alx@kernel.org>
---
 arch/s390/lib/test_unwind.c | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/arch/s390/lib/test_unwind.c b/arch/s390/lib/test_unwind.c
index 6bb3fa5bf925..bb47958f5aed 100644
--- a/arch/s390/lib/test_unwind.c
+++ b/arch/s390/lib/test_unwind.c
@@ -47,7 +47,8 @@ static void print_backtrace(char *bt)
 static noinline int test_unwind(struct task_struct *task, struct pt_regs *regs,
 				unsigned long sp)
 {
-	int frame_count, prev_is_func2, seen_func2_func1, seen_arch_rethook_trampoline;
+	int frame_count;
+	bool prev_is_func2, seen_func2_func1, seen_arch_rethook_trampoline;
 	const int max_frames = 128;
 	struct unwind_state state;
 	size_t bt_pos = 0;
@@ -61,9 +62,9 @@ static noinline int test_unwind(struct task_struct *task, struct pt_regs *regs,
 	}
 	/* Unwind. */
 	frame_count = 0;
-	prev_is_func2 = 0;
-	seen_func2_func1 = 0;
-	seen_arch_rethook_trampoline = 0;
+	prev_is_func2 = false;
+	seen_func2_func1 = false;
+	seen_arch_rethook_trampoline = false;
 	unwind_for_each_frame(&state, task, regs, sp) {
 		unsigned long addr = unwind_get_return_address(&state);
 		char sym[KSYM_SYMBOL_LEN];
@@ -87,10 +88,10 @@ static noinline int test_unwind(struct task_struct *task, struct pt_regs *regs,
 		}
 		frame_count += 1;
 		if (prev_is_func2 && str_has_prefix(sym, "unwindme_func1"))
-			seen_func2_func1 = 1;
+			seen_func2_func1 = true;
 		prev_is_func2 = str_has_prefix(sym, "unwindme_func2");
 		if (str_has_prefix(sym, "arch_rethook_trampoline+0x0/"))
-			seen_arch_rethook_trampoline = 1;
+			seen_arch_rethook_trampoline = true;
 	}
 
 	/* Check the results. */
-- 
2.55.0


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

^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v6 14/19] treewide: Replace boolean uses of str_has_prefix() by strprefix()
  2026-11-14 15:21 [PATCH v6 00/19] Add strprefix() and fix a bug with it Alejandro Colomar
                   ` (12 preceding siblings ...)
  2026-11-14 15:23 ` [PATCH v6 13/19] s390/test_unwind: Use bool for booleans Alejandro Colomar
@ 2026-11-14 15:23 ` Alejandro Colomar
  2026-11-14 15:23 ` [PATCH v6 15/19] treewide: Replace some " Alejandro Colomar
                   ` (4 subsequent siblings)
  18 siblings, 0 replies; 37+ messages in thread
From: Alejandro Colomar @ 2026-11-14 15:23 UTC (permalink / raw)
  To: linux-hardening
  Cc: Alejandro Colomar, Krzysztof Kozlowski, Maciej W. Rozycki,
	Geert Uytterhoeven, Andreas Schwab, Andy Shevchenko,
	Bartosz Golaszewski, Daniel Scally, Danilo Krummrich,
	Greg Kroah-Hartman, Hans Verkuil, Heikki Krogerus, Joe Perches,
	Johan Hovold, Kees Cook, Laurent Pinchart, Len Brown,
	Linus Torvalds, Linus Walleij, Namhyung Kim, Rafael J. Wysocki,
	Rob Herring, Sakari Ailus, Saravana Kannan, Steven Rostedt,
	Tom Zanussi, linux-acpi, driver-core, linux-kernel, devicetree,
	linux-gpio

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

str_has_prefix() doesn't work well for empty prefixes (see link below),
so this might actually fix some bugs silently where the prefix is an
empty string.

Link: https://lore.kernel.org/linux-hardening/20261104-fwnode-for-each-prop-v1-1-6e28fe1c72c4@oss.qualcomm.com/
Signed-off-by: Alejandro Colomar <alx@kernel.org>
---
 arch/alpha/kernel/setup.c                     |  2 +-
 arch/parisc/kernel/setup.c                    |  2 +-
 arch/powerpc/platforms/powernv/pci-ioda.c     |  2 +-
 arch/s390/kernel/early_printk.c               |  2 +-
 arch/s390/kernel/module.c                     |  6 ++---
 arch/s390/kernel/setup.c                      |  4 ++--
 arch/s390/lib/test_unwind.c                   |  6 ++---
 drivers/gpio/gpio-aggregator.c                |  2 +-
 drivers/gpu/drm/xe/xe_configfs.c              |  6 ++---
 drivers/iio/pressure/hsc030pa.c               |  2 +-
 drivers/md/dm-inlinecrypt.c                   |  2 +-
 drivers/media/i2c/ds90ub960.c                 |  2 +-
 drivers/remoteproc/mtk_scp.c                  |  2 +-
 .../media/atomisp/pci/atomisp_csi2_bridge.c   |  2 +-
 drivers/tty/serial/serial_base_bus.c          |  4 ++--
 kernel/debug/kdb/kdb_main.c                   |  2 +-
 kernel/kcsan/debugfs.c                        |  2 +-
 kernel/kcsan/report.c                         |  2 +-
 kernel/kprobes.c                              |  4 ++--
 kernel/locking/locktorture.c                  |  8 +++----
 kernel/power/main.c                           |  2 +-
 kernel/trace/trace_events.c                   |  2 +-
 kernel/trace/trace_events_hist.c              | 14 ++++++------
 kernel/trace/trace_events_synth.c             |  2 +-
 kernel/trace/trace_events_user.c              | 22 +++++++++----------
 kernel/trace/trace_probe.c                    | 22 +++++++++----------
 mm/arch_numa.c                                |  2 +-
 mm/kfence/report.c                            | 14 ++++++------
 sound/soc/sdw_utils/soc_sdw_senary_sdca.c     |  2 +-
 29 files changed, 73 insertions(+), 73 deletions(-)

diff --git a/arch/alpha/kernel/setup.c b/arch/alpha/kernel/setup.c
index 4cbcfb1852d5..79e63580b419 100644
--- a/arch/alpha/kernel/setup.c
+++ b/arch/alpha/kernel/setup.c
@@ -452,7 +452,7 @@ setup_arch(char **cmdline_p)
 #ifndef alpha_using_srm
 	/* Assume that we've booted from SRM if we haven't booted from MILO.
 	   Detect the later by looking for "MILO" in the system serial nr.  */
-	alpha_using_srm = !str_has_prefix((const char *)hwrpb->ssn, "MILO");
+	alpha_using_srm = !strprefix((const char *)hwrpb->ssn, "MILO");
 #endif
 #ifndef alpha_using_qemu
 	/* Similarly, look for QEMU.  */
diff --git a/arch/parisc/kernel/setup.c b/arch/parisc/kernel/setup.c
index 4d3015a411d6..6cd1b65276b5 100644
--- a/arch/parisc/kernel/setup.c
+++ b/arch/parisc/kernel/setup.c
@@ -56,7 +56,7 @@ static void __init setup_cmdline(char **cmdline_p)
 
 	/* autodetect console type (if not done by palo yet) */
 	p = boot_command_line;
-	if (!str_has_prefix(p, "console=") && !strstr(p, " console=")) {
+	if (!strprefix(p, "console=") && !strstr(p, " console=")) {
 		strlcat(p, " console=", COMMAND_LINE_SIZE);
 		if (PAGE0->mem_cons.cl_class == CL_DUPLEX)
 			strlcat(p, "ttyS0", COMMAND_LINE_SIZE);
diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c
index 728a5610d167..1cf0b4afe3df 100644
--- a/arch/powerpc/platforms/powernv/pci-ioda.c
+++ b/arch/powerpc/platforms/powernv/pci-ioda.c
@@ -1629,7 +1629,7 @@ int64_t pnv_opal_pci_msi_eoi(struct irq_data *d)
  */
 bool is_pnv_opal_msi(struct irq_chip *chip)
 {
-	return chip && chip->name && str_has_prefix(chip->name, "PNV-");
+	return chip && chip->name && strprefix(chip->name, "PNV-");
 }
 EXPORT_SYMBOL_GPL(is_pnv_opal_msi);
 
diff --git a/arch/s390/kernel/early_printk.c b/arch/s390/kernel/early_printk.c
index cefe020a3be3..095e282c1e06 100644
--- a/arch/s390/kernel/early_printk.c
+++ b/arch/s390/kernel/early_printk.c
@@ -36,7 +36,7 @@ static int __init setup_early_printk(char *buf)
 	if (early_console)
 		return 0;
 	/* Accept only "earlyprintk" and "earlyprintk=sclp" */
-	if (buf && !str_has_prefix(buf, "sclp"))
+	if (buf && !strprefix(buf, "sclp"))
 		return 0;
 	register_early_console();
 	return 0;
diff --git a/arch/s390/kernel/module.c b/arch/s390/kernel/module.c
index 9d1f8a50f5a4..e9734d2b25f3 100644
--- a/arch/s390/kernel/module.c
+++ b/arch/s390/kernel/module.c
@@ -520,15 +520,15 @@ int module_finalize(const Elf_Ehdr *hdr,
 			apply_alternatives(aseg, aseg + s->sh_size);
 
 		if (IS_ENABLED(CONFIG_EXPOLINE) &&
-		    (str_has_prefix(secname, ".s390_indirect")))
+		    (strprefix(secname, ".s390_indirect")))
 			nospec_revert(aseg, aseg + s->sh_size);
 
 		if (IS_ENABLED(CONFIG_EXPOLINE) &&
-		    (str_has_prefix(secname, ".s390_return")))
+		    (strprefix(secname, ".s390_return")))
 			nospec_revert(aseg, aseg + s->sh_size);
 
 		if (IS_ENABLED(CONFIG_STACKPROTECTOR) &&
-		    (str_has_prefix(secname, "__stack_protector_loc"))) {
+		    (strprefix(secname, "__stack_protector_loc"))) {
 			rc = stack_protector_apply(aseg, aseg + s->sh_size);
 			if (rc)
 				break;
diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c
index b60284328fe3..7074cdd58aa3 100644
--- a/arch/s390/kernel/setup.c
+++ b/arch/s390/kernel/setup.c
@@ -269,7 +269,7 @@ static void __init conmode_default(void)
 #endif
 			return;
 		}
-		if (str_has_prefix(ptr + 8, "3270")) {
+		if (strprefix(ptr + 8, "3270")) {
 #if defined(CONFIG_TN3270_CONSOLE)
 			SET_CONSOLE_3270;
 #elif defined(CONFIG_TN3215_CONSOLE)
@@ -277,7 +277,7 @@ static void __init conmode_default(void)
 #elif defined(CONFIG_SCLP_CONSOLE) || defined(CONFIG_SCLP_VT220_CONSOLE)
 			SET_CONSOLE_SCLP;
 #endif
-		} else if (str_has_prefix(ptr + 8, "3215")) {
+		} else if (strprefix(ptr + 8, "3215")) {
 #if defined(CONFIG_TN3215_CONSOLE)
 			SET_CONSOLE_3215;
 #elif defined(CONFIG_TN3270_CONSOLE)
diff --git a/arch/s390/lib/test_unwind.c b/arch/s390/lib/test_unwind.c
index bb47958f5aed..8d6b6efe02fc 100644
--- a/arch/s390/lib/test_unwind.c
+++ b/arch/s390/lib/test_unwind.c
@@ -87,10 +87,10 @@ static noinline int test_unwind(struct task_struct *task, struct pt_regs *regs,
 				kunit_err(current_test, "backtrace buffer is too small\n");
 		}
 		frame_count += 1;
-		if (prev_is_func2 && str_has_prefix(sym, "unwindme_func1"))
+		if (prev_is_func2 && strprefix(sym, "unwindme_func1"))
 			seen_func2_func1 = true;
-		prev_is_func2 = str_has_prefix(sym, "unwindme_func2");
-		if (str_has_prefix(sym, "arch_rethook_trampoline+0x0/"))
+		prev_is_func2 = strprefix(sym, "unwindme_func2");
+		if (strprefix(sym, "arch_rethook_trampoline+0x0/"))
 			seen_arch_rethook_trampoline = true;
 	}
 
diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c
index b0b65416d74d..efa92e925461 100644
--- a/drivers/gpio/gpio-aggregator.c
+++ b/drivers/gpio/gpio-aggregator.c
@@ -1546,7 +1546,7 @@ static ssize_t gpio_aggregator_delete_device_store(struct device_driver *driver,
 	unsigned int id;
 	int error;
 
-	if (!str_has_prefix(buf, DRV_NAME "."))
+	if (!strprefix(buf, DRV_NAME "."))
 		return -EINVAL;
 
 	error = kstrtouint(buf + strlen(DRV_NAME "."), 10, &id);
diff --git a/drivers/gpu/drm/xe/xe_configfs.c b/drivers/gpu/drm/xe/xe_configfs.c
index 2ddd707a6dbd..8bd11f5bdfa1 100644
--- a/drivers/gpu/drm/xe/xe_configfs.c
+++ b/drivers/gpu/drm/xe/xe_configfs.c
@@ -521,7 +521,7 @@ static const struct engine_info *lookup_engine_info(const char *pattern, u64 *ma
 		u8 instance;
 		u16 bit;
 
-		if (!str_has_prefix(pattern, engine_info[i].cls))
+		if (!strprefix(pattern, engine_info[i].cls))
 			continue;
 
 		pattern += strlen(engine_info[i].cls);
@@ -836,7 +836,7 @@ static ssize_t parse_wa_bb_lines(const char *lines,
 		p += ret;
 		p += strspn(p, " \t");
 
-		if (str_has_prefix(p, "cmd")) {
+		if (strprefix(p, "cmd")) {
 			for (p += strlen("cmd"); *p;) {
 				ret = parse_hex(p, &val);
 				if (ret < 0)
@@ -848,7 +848,7 @@ static ssize_t parse_wa_bb_lines(const char *lines,
 				dwords++;
 				wa_bb_append(&wa_bb[info->engine_class], val);
 			}
-		} else if (str_has_prefix(p, "reg")) {
+		} else if (strprefix(p, "reg")) {
 			p += strlen("reg");
 			ret = parse_hex(p, &val);
 			if (ret <= 0)
diff --git a/drivers/iio/pressure/hsc030pa.c b/drivers/iio/pressure/hsc030pa.c
index 0374d406b401..eaf5ee190be5 100644
--- a/drivers/iio/pressure/hsc030pa.c
+++ b/drivers/iio/pressure/hsc030pa.c
@@ -476,7 +476,7 @@ int hsc_common_probe(struct device *dev, hsc_recv_fn recv)
 		return dev_err_probe(dev, ret,
 			     "honeywell,pressure-triplet could not be read\n");
 
-	if (str_has_prefix(triplet, "NA")) {
+	if (strprefix(triplet, "NA")) {
 		ret = device_property_read_u32(dev, "honeywell,pmin-pascal",
 					       &hsc->pmin);
 		if (ret)
diff --git a/drivers/md/dm-inlinecrypt.c b/drivers/md/dm-inlinecrypt.c
index 3479dfe5f708..4a1cc2da4c29 100644
--- a/drivers/md/dm-inlinecrypt.c
+++ b/drivers/md/dm-inlinecrypt.c
@@ -257,7 +257,7 @@ static int inlinecrypt_ctr_optional(struct dm_target *ti,
 			ti->error = "Not enough feature arguments";
 			return -EINVAL;
 		}
-		if (str_has_prefix(opt_string, "keytype:")) {
+		if (strprefix(opt_string, "keytype:")) {
 			const char *val = opt_string + strlen("keytype:");
 
 			if (!*val) {
diff --git a/drivers/media/i2c/ds90ub960.c b/drivers/media/i2c/ds90ub960.c
index 7d7ba6d349fc..725245a1680b 100644
--- a/drivers/media/i2c/ds90ub960.c
+++ b/drivers/media/i2c/ds90ub960.c
@@ -4692,7 +4692,7 @@ ub960_fwnode_get_link_by_regs(struct fwnode_handle *links_fwnode,
 	fwnode_for_each_child_node(links_fwnode, link_fwnode) {
 		u32 link_num;
 
-		if (!str_has_prefix(fwnode_get_name(link_fwnode), "link@"))
+		if (!strprefix(fwnode_get_name(link_fwnode), "link@"))
 			continue;
 
 		ret = fwnode_property_read_u32(link_fwnode, "reg", &link_num);
diff --git a/drivers/remoteproc/mtk_scp.c b/drivers/remoteproc/mtk_scp.c
index 9751acc2bf83..2dd18b34a378 100644
--- a/drivers/remoteproc/mtk_scp.c
+++ b/drivers/remoteproc/mtk_scp.c
@@ -1147,7 +1147,7 @@ static const char *scp_get_default_fw_path(struct device *dev, int core_id)
 		return ERR_PTR(-EINVAL);
 
 	/* If the compatible string starts with "mediatek,mt" assume that it's ok */
-	if (!str_has_prefix(compatible, "mediatek,mt"))
+	if (!strprefix(compatible, "mediatek,mt"))
 		return ERR_PTR(-EINVAL);
 
 	if (core_id >= 0)
diff --git a/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c b/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c
index cca91c6d71a5..2cfe5f92ca3b 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c
@@ -222,7 +222,7 @@ static int atomisp_csi2_get_pmc_clk_nr_from_acpi_pr0(struct acpi_device *adev)
 
 		acpi_get_name(rhandle, ACPI_SINGLE_NAME, &b_name);
 
-		if (str_has_prefix(name, "CLK") && !kstrtou8(&name[3], 10, &clock_num) &&
+		if (strprefix(name, "CLK") && !kstrtou8(&name[3], 10, &clock_num) &&
 		    clock_num <= 4) {
 			ret = clock_num;
 			break;
diff --git a/drivers/tty/serial/serial_base_bus.c b/drivers/tty/serial/serial_base_bus.c
index 5f23284a8778..1a5512457b74 100644
--- a/drivers/tty/serial/serial_base_bus.c
+++ b/drivers/tty/serial/serial_base_bus.c
@@ -33,11 +33,11 @@ static const struct device_type serial_port_type = {
 static int serial_base_match(struct device *dev, const struct device_driver *drv)
 {
 	if (dev->type == &serial_ctrl_type &&
-	    str_has_prefix(drv->name, serial_ctrl_type.name))
+	    strprefix(drv->name, serial_ctrl_type.name))
 		return 1;
 
 	if (dev->type == &serial_port_type &&
-	    str_has_prefix(drv->name, serial_port_type.name))
+	    strprefix(drv->name, serial_port_type.name))
 		return 1;
 
 	return 0;
diff --git a/kernel/debug/kdb/kdb_main.c b/kernel/debug/kdb/kdb_main.c
index 40c332d2179d..d3c8afaae4b3 100644
--- a/kernel/debug/kdb/kdb_main.c
+++ b/kernel/debug/kdb/kdb_main.c
@@ -803,7 +803,7 @@ static void parse_grep(const char *str)
 	cp++;
 	while (isspace(*cp))
 		cp++;
-	if (!str_has_prefix(cp, "grep ")) {
+	if (!strprefix(cp, "grep ")) {
 		kdb_printf("invalid 'pipe', see grephelp\n");
 		return;
 	}
diff --git a/kernel/kcsan/debugfs.c b/kernel/kcsan/debugfs.c
index 2af39ba5b70b..b945f9ab39a3 100644
--- a/kernel/kcsan/debugfs.c
+++ b/kernel/kcsan/debugfs.c
@@ -233,7 +233,7 @@ debugfs_write(struct file *file, const char __user *buf, size_t count, loff_t *o
 		WRITE_ONCE(kcsan_enabled, true);
 	} else if (!strcmp(arg, "off")) {
 		WRITE_ONCE(kcsan_enabled, false);
-	} else if (str_has_prefix(arg, "microbench=")) {
+	} else if (strprefix(arg, "microbench=")) {
 		unsigned long iters;
 
 		if (kstrtoul(&arg[strlen("microbench=")], 0, &iters))
diff --git a/kernel/kcsan/report.c b/kernel/kcsan/report.c
index 11a48b78f8d1..00f9b12f6a6d 100644
--- a/kernel/kcsan/report.c
+++ b/kernel/kcsan/report.c
@@ -292,7 +292,7 @@ static int get_stack_skipnr(const unsigned long stack_entries[], int num_entries
 		cur = strnstr(buf, "kcsan_", len);
 		if (cur) {
 			cur += strlen("kcsan_");
-			if (!str_has_prefix(cur, "test"))
+			if (!strprefix(cur, "test"))
 				continue; /* KCSAN runtime function. */
 			/* KCSAN related test. */
 		}
diff --git a/kernel/kprobes.c b/kernel/kprobes.c
index 2441b9e39343..e8ce9ad73389 100644
--- a/kernel/kprobes.c
+++ b/kernel/kprobes.c
@@ -1860,8 +1860,8 @@ static bool is_cfi_preamble_symbol(unsigned long addr)
 	if (lookup_symbol_name(addr, symbuf))
 		return false;
 
-	return str_has_prefix(symbuf, "__cfi_") ||
-		str_has_prefix(symbuf, "__pfx_");
+	return strprefix(symbuf, "__cfi_") ||
+		strprefix(symbuf, "__pfx_");
 }
 
 static int check_kprobe_address_safe(struct kprobe *p,
diff --git a/kernel/locking/locktorture.c b/kernel/locking/locktorture.c
index e618bcf75e2d..5408c4de5b01 100644
--- a/kernel/locking/locktorture.c
+++ b/kernel/locking/locktorture.c
@@ -1277,16 +1277,16 @@ static int __init lock_torture_init(void)
 	}
 
 #ifdef CONFIG_DEBUG_MUTEXES
-	if (str_has_prefix(torture_type, "mutex"))
+	if (strprefix(torture_type, "mutex"))
 		cxt.debug_lock = true;
 #endif
 #ifdef CONFIG_DEBUG_RT_MUTEXES
-	if (str_has_prefix(torture_type, "rtmutex"))
+	if (strprefix(torture_type, "rtmutex"))
 		cxt.debug_lock = true;
 #endif
 #ifdef CONFIG_DEBUG_SPINLOCK
-	if ((str_has_prefix(torture_type, "spin")) ||
-	    (str_has_prefix(torture_type, "rw_lock")))
+	if ((strprefix(torture_type, "spin")) ||
+	    (strprefix(torture_type, "rw_lock")))
 		cxt.debug_lock = true;
 #endif
 
diff --git a/kernel/power/main.c b/kernel/power/main.c
index 5429e9f19b65..763f84900e4b 100644
--- a/kernel/power/main.c
+++ b/kernel/power/main.c
@@ -781,7 +781,7 @@ static suspend_state_t decode_state(const char *buf, size_t n)
 	len = p ? p - buf : n;
 
 	/* Check hibernation first. */
-	if (len == 4 && str_has_prefix(buf, "disk"))
+	if (len == 4 && strprefix(buf, "disk"))
 		return PM_SUSPEND_MAX;
 
 #ifdef CONFIG_SUSPEND
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index 49d135bc2fbf..d467cdf9c805 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -2182,7 +2182,7 @@ static int f_show(struct seq_file *m, void *v)
 	 */
 	array_descriptor = strchr(field->type, '[');
 
-	if (str_has_prefix(field->type, "__data_loc"))
+	if (strprefix(field->type, "__data_loc"))
 		array_descriptor = NULL;
 
 	if (!array_descriptor)
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
index 1aae7d7c6dc5..c099bfbb9fb6 100644
--- a/kernel/trace/trace_events_hist.c
+++ b/kernel/trace/trace_events_hist.c
@@ -1468,9 +1468,9 @@ static int parse_action(char *str, struct hist_trigger_attrs *attrs)
 	if (attrs->n_actions >= HIST_ACTIONS_MAX)
 		return ret;
 
-	if ((str_has_prefix(str, "onmatch(")) ||
-	    (str_has_prefix(str, "onmax(")) ||
-	    (str_has_prefix(str, "onchange("))) {
+	if ((strprefix(str, "onmatch(")) ||
+	    (strprefix(str, "onmax(")) ||
+	    (strprefix(str, "onchange("))) {
 		attrs->action_str[attrs->n_actions] = kstrdup(str, GFP_KERNEL);
 		if (!attrs->action_str[attrs->n_actions]) {
 			ret = -ENOMEM;
@@ -1508,7 +1508,7 @@ static int parse_assignment(struct trace_array *tr,
 			ret = -ENOMEM;
 			goto out;
 		}
-	} else if (str_has_prefix(str, "name=")) {
+	} else if (strprefix(str, "name=")) {
 		attrs->name = kstrdup(str, GFP_KERNEL);
 		if (!attrs->name) {
 			ret = -ENOMEM;
@@ -3775,7 +3775,7 @@ static int action_parse(struct trace_array *tr, char *str, struct action_data *d
 		goto out;
 	}
 
-	if (str_has_prefix(action_name, "save")) {
+	if (strprefix(action_name, "save")) {
 		char *params = strsep(&str, ")");
 
 		if (!params) {
@@ -3801,7 +3801,7 @@ static int action_parse(struct trace_array *tr, char *str, struct action_data *d
 		data->track_data.save_data = save_track_data_vars;
 		data->fn = ontrack_action;
 		data->action = ACTION_SAVE;
-	} else if (str_has_prefix(action_name, "snapshot")) {
+	} else if (strprefix(action_name, "snapshot")) {
 		char *params = strsep(&str, ")");
 
 		if (!str) {
@@ -3826,7 +3826,7 @@ static int action_parse(struct trace_array *tr, char *str, struct action_data *d
 	} else {
 		char *params = strsep(&str, ")");
 
-		if (str_has_prefix(action_name, "trace"))
+		if (strprefix(action_name, "trace"))
 			data->use_trace_keyword = true;
 
 		if (params) {
diff --git a/kernel/trace/trace_events_synth.c b/kernel/trace/trace_events_synth.c
index e2870212e58d..016ca522e5af 100644
--- a/kernel/trace/trace_events_synth.c
+++ b/kernel/trace/trace_events_synth.c
@@ -167,7 +167,7 @@ static int synth_event_define_fields(struct trace_event_call *call)
 
 static bool synth_field_signed(char *type)
 {
-	if (str_has_prefix(type, "u"))
+	if (strprefix(type, "u"))
 		return false;
 	if (strcmp(type, "gfp_t") == 0)
 		return false;
diff --git a/kernel/trace/trace_events_user.c b/kernel/trace/trace_events_user.c
index f658c3a77aa7..8669f11fb9bd 100644
--- a/kernel/trace/trace_events_user.c
+++ b/kernel/trace/trace_events_user.c
@@ -1098,13 +1098,13 @@ static int user_field_size(const char *type)
 		return sizeof(char);
 	if (strcmp(type, "unsigned char") == 0)
 		return sizeof(unsigned char);
-	if (str_has_prefix(type, "char["))
+	if (strprefix(type, "char["))
 		return user_field_array_size(type);
-	if (str_has_prefix(type, "unsigned char["))
+	if (strprefix(type, "unsigned char["))
 		return user_field_array_size(type);
-	if (str_has_prefix(type, "__data_loc "))
+	if (strprefix(type, "__data_loc "))
 		return sizeof(u32);
-	if (str_has_prefix(type, "__rel_loc "))
+	if (strprefix(type, "__rel_loc "))
 		return sizeof(u32);
 
 	/* Unknown basic type, error */
@@ -1145,10 +1145,10 @@ static int user_event_add_field(struct user_event *user, const char *type,
 	if (!field)
 		return -ENOMEM;
 
-	if (str_has_prefix(type, "__data_loc "))
+	if (strprefix(type, "__data_loc "))
 		goto add_validator;
 
-	if (str_has_prefix(type, "__rel_loc ")) {
+	if (strprefix(type, "__rel_loc ")) {
 		validator_flags |= VALIDATOR_REL;
 		goto add_validator;
 	}
@@ -1349,12 +1349,12 @@ static const char *user_field_format(const char *type)
 
 static bool user_field_is_dyn_string(const char *type, const char **str_func)
 {
-	if (str_has_prefix(type, "__data_loc ")) {
+	if (strprefix(type, "__data_loc ")) {
 		*str_func = "__get_str";
 		goto check;
 	}
 
-	if (str_has_prefix(type, "__rel_loc ")) {
+	if (strprefix(type, "__rel_loc ")) {
 		*str_func = "__get_rel_str";
 		goto check;
 	}
@@ -1401,7 +1401,7 @@ static int user_field_set_string(struct ftrace_event_field *field,
 	pos += snprintf(buf + pos, LEN_OR_ZERO, " ");
 	pos += snprintf(buf + pos, LEN_OR_ZERO, "%s", field->name);
 
-	if (str_has_prefix(field->type, "struct "))
+	if (strprefix(field->type, "struct "))
 		pos += snprintf(buf + pos, LEN_OR_ZERO, " %d", field->size);
 
 	if (colon)
@@ -1808,7 +1808,7 @@ static int user_event_create(const char *raw_command)
 	char *name;
 	int ret;
 
-	if (!str_has_prefix(raw_command, USER_EVENTS_PREFIX))
+	if (!strprefix(raw_command, USER_EVENTS_PREFIX))
 		return -ECANCELED;
 
 	raw_command += USER_EVENTS_PREFIX_LEN;
@@ -1861,7 +1861,7 @@ static int user_event_show(struct seq_file *m, struct dyn_event *ev)
 
 		seq_printf(m, "%s %s", field->type, field->name);
 
-		if (str_has_prefix(field->type, "struct "))
+		if (strprefix(field->type, "struct "))
 			seq_printf(m, " %d", field->size);
 
 		depth++;
diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c
index a880a90642d7..62acdf737c12 100644
--- a/kernel/trace/trace_probe.c
+++ b/kernel/trace/trace_probe.c
@@ -370,10 +370,10 @@ static int parse_this_cpu(char *arg, struct traceprobe_parse_context *ctx)
 		trace_probe_log_err(ctx->offset, NOSUP_PERCPU);
 		return -EINVAL;
 	}
-	if (str_has_prefix(arg, THIS_CPU_PTR_PREFIX)) {
+	if (strprefix(arg, THIS_CPU_PTR_PREFIX)) {
 		arg += THIS_CPU_PTR_LEN;
 		ctx->offset += THIS_CPU_PTR_LEN;
-	} else if (str_has_prefix(arg, THIS_CPU_READ_PREFIX)) {
+	} else if (strprefix(arg, THIS_CPU_READ_PREFIX)) {
 		arg += THIS_CPU_READ_LEN;
 		ctx->offset += THIS_CPU_READ_LEN;
 		is_read = true;
@@ -745,7 +745,7 @@ static int parse_btf_arg(char *varname,
 	u32 tid;
 
 	/* Note: field is not separated at this point, so check prefix. */
-	if (!str_has_prefix(varname, "$current") &&
+	if (!strprefix(varname, "$current") &&
 	    !ctx->funcname && !(ctx->flags & TPARG_FL_TEVENT))
 		return -EINVAL;
 
@@ -1001,8 +1001,8 @@ static int handle_typecast(char *arg, struct traceprobe_parse_context *ctx)
 		ctx->offset += 1;
 		tmp++;
 	} else if (*tmp == '+' || *tmp == '-' ||
-		   str_has_prefix(tmp, THIS_CPU_PTR_PREFIX) ||
-		   str_has_prefix(tmp, THIS_CPU_READ_PREFIX)) {
+		   strprefix(tmp, THIS_CPU_PTR_PREFIX) ||
+		   strprefix(tmp, THIS_CPU_READ_PREFIX)) {
 		/* Dereference can have another field access inside it. */
 		char *open = strchr(tmp + 1, '(');
 
@@ -1412,7 +1412,7 @@ static int parse_probe_vars(char *orig_arg, const struct fetch_type *t,
 	if (ctx->flags & TPARG_FL_TEVENT)
 		goto inval;
 
-	if (str_has_prefix(arg, "retval"))
+	if (strprefix(arg, "retval"))
 		return parse_probe_var_retval(orig_arg, pcode, end, ctx);
 
 	len = str_has_prefix(arg, "stack");
@@ -1420,7 +1420,7 @@ static int parse_probe_vars(char *orig_arg, const struct fetch_type *t,
 		return parse_probe_var_stack(arg, len, code, ctx);
 
 	/* $current returns the address of the current task_struct. */
-	if (str_has_prefix(arg, "current"))
+	if (strprefix(arg, "current"))
 		return parse_probe_var_current(orig_arg, arg, pcode, end, ctx);
 
 	len = str_has_prefix(arg, "arg");
@@ -1642,8 +1642,8 @@ static int parse_probe_arg_nested(char **parg, struct traceprobe_parse_context *
 	while (true) {
 		/* Determine if this is a nested argument */
 		if (arg[0] != '+' && arg[0] != '-' && arg[0] != '(' &&
-		    !str_has_prefix(arg, THIS_CPU_PTR_PREFIX) &&
-		    !str_has_prefix(arg, THIS_CPU_READ_PREFIX))
+		    !strprefix(arg, THIS_CPU_PTR_PREFIX) &&
+		    !strprefix(arg, THIS_CPU_READ_PREFIX))
 			break;
 
 		/* If nested, check the maximum depth limit */
@@ -2228,7 +2228,7 @@ static int argv_has_var_arg(int argc, const char *argv[], int *args_idx,
 	int i, found = 0;
 
 	for (i = 0; i < argc; i++)
-		if (str_has_prefix(argv[i], "$arg")) {
+		if (strprefix(argv[i], "$arg")) {
 			trace_probe_log_set_index(i + 2);
 
 			if (!tparg_is_function_entry(ctx->flags) &&
@@ -2335,7 +2335,7 @@ const char **traceprobe_expand_meta_args(int argc, const char *argv[],
 			continue;
 		}
 
-		if (str_has_prefix(argv[i], "$arg")) {
+		if (strprefix(argv[i], "$arg")) {
 			char *type = NULL;
 
 			n = simple_strtoul(argv[i] + 4, &type, 10);
diff --git a/mm/arch_numa.c b/mm/arch_numa.c
index a4b1732274fa..8380b8fe11d5 100644
--- a/mm/arch_numa.c
+++ b/mm/arch_numa.c
@@ -24,7 +24,7 @@ static __init int numa_parse_early_param(char *opt)
 {
 	if (!opt)
 		return -EINVAL;
-	if (str_has_prefix(opt, "off"))
+	if (strprefix(opt, "off"))
 		numa_off = true;
 	if (!strncmp(opt, "fake=", 5))
 		return numa_emu_cmdline(opt + 5);
diff --git a/mm/kfence/report.c b/mm/kfence/report.c
index d548536864b1..e6fae0c2b9b5 100644
--- a/mm/kfence/report.c
+++ b/mm/kfence/report.c
@@ -97,9 +97,9 @@ static int get_stack_skipnr(const unsigned long stack_entries[], int num_entries
 	for (skipnr = 0; skipnr < num_entries; skipnr++) {
 		int len = scnprintf(buf, sizeof(buf), "%ps", (void *)stack_entries[skipnr]);
 
-		if (str_has_prefix(buf, ARCH_FUNC_PREFIX "kfence_") ||
-		    str_has_prefix(buf, ARCH_FUNC_PREFIX "__kfence_") ||
-		    str_has_prefix(buf, ARCH_FUNC_PREFIX "__kmem_cache_free") ||
+		if (strprefix(buf, ARCH_FUNC_PREFIX "kfence_") ||
+		    strprefix(buf, ARCH_FUNC_PREFIX "__kfence_") ||
+		    strprefix(buf, ARCH_FUNC_PREFIX "__kmem_cache_free") ||
 		    !strncmp(buf, ARCH_FUNC_PREFIX "__slab_free", len)) {
 			/*
 			 * In case of tail calls from any of the below to any of
@@ -114,10 +114,10 @@ static int get_stack_skipnr(const unsigned long stack_entries[], int num_entries
 		 * into the slab allocators. Includes the *_bulk() variants by
 		 * checking prefixes.
 		 */
-		if (str_has_prefix(buf, ARCH_FUNC_PREFIX "kfree") ||
-		    str_has_prefix(buf, ARCH_FUNC_PREFIX "kmem_cache_free") ||
-		    str_has_prefix(buf, ARCH_FUNC_PREFIX "__kmalloc") ||
-		    str_has_prefix(buf, ARCH_FUNC_PREFIX "kmem_cache_alloc"))
+		if (strprefix(buf, ARCH_FUNC_PREFIX "kfree") ||
+		    strprefix(buf, ARCH_FUNC_PREFIX "kmem_cache_free") ||
+		    strprefix(buf, ARCH_FUNC_PREFIX "__kmalloc") ||
+		    strprefix(buf, ARCH_FUNC_PREFIX "kmem_cache_alloc"))
 			goto found;
 	}
 	if (fallback < num_entries)
diff --git a/sound/soc/sdw_utils/soc_sdw_senary_sdca.c b/sound/soc/sdw_utils/soc_sdw_senary_sdca.c
index 5c2cd75307c9..afaca66e41ef 100644
--- a/sound/soc/sdw_utils/soc_sdw_senary_sdca.c
+++ b/sound/soc/sdw_utils/soc_sdw_senary_sdca.c
@@ -33,7 +33,7 @@ static const struct codec_route_map codec_routes[] = {
 static const struct codec_route_map *get_codec_route_map(const char *dai_name)
 {
 	for (size_t i = 0; i < ARRAY_SIZE(codec_routes); i++) {
-		if (str_has_prefix(dai_name, codec_routes[i].codec_name))
+		if (strprefix(dai_name, codec_routes[i].codec_name))
 			return &codec_routes[i];
 	}
 	return NULL;
-- 
2.55.0


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

^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v6 15/19] treewide: Replace some uses of str_has_prefix() by strprefix()
  2026-11-14 15:21 [PATCH v6 00/19] Add strprefix() and fix a bug with it Alejandro Colomar
                   ` (13 preceding siblings ...)
  2026-11-14 15:23 ` [PATCH v6 14/19] treewide: Replace boolean uses of str_has_prefix() by strprefix() Alejandro Colomar
@ 2026-11-14 15:23 ` Alejandro Colomar
  2026-11-14 15:34   ` sashiko-bot
  2026-11-14 15:23 ` [PATCH v6 16/19] string: Add strsuffix() Alejandro Colomar
                   ` (3 subsequent siblings)
  18 siblings, 1 reply; 37+ messages in thread
From: Alejandro Colomar @ 2026-11-14 15:23 UTC (permalink / raw)
  To: linux-hardening
  Cc: Alejandro Colomar, Krzysztof Kozlowski, Maciej W. Rozycki,
	Geert Uytterhoeven, Andreas Schwab, Andy Shevchenko,
	Bartosz Golaszewski, Daniel Scally, Danilo Krummrich,
	Greg Kroah-Hartman, Hans Verkuil, Heikki Krogerus, Joe Perches,
	Johan Hovold, Kees Cook, Laurent Pinchart, Len Brown,
	Linus Torvalds, Linus Walleij, Namhyung Kim, Rafael J. Wysocki,
	Rob Herring, Sakari Ailus, Saravana Kannan, Steven Rostedt,
	Tom Zanussi, linux-acpi, driver-core, linux-kernel, devicetree,
	linux-gpio

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

In many cases, it results in simpler code.  For example:

	-       prefix_len = str_has_prefix(name, PDSC_FW_COMPONENT_PREFIX);
	-       if (prefix_len)
	-               name += prefix_len;
	+       name = strprefix(name, PDSC_FW_COMPONENT_PREFIX) ?: name;

It avoids explicit pointer arithmetic, which also prevents certain bugs.

After this commit, only a few uses of str_has_prefix() remain.  Those
are difficult to remove, as the surrounding code is deeply based on
lengths.  Let's leave those, as it's too risky to touch that code.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
---
 drivers/net/ethernet/amd/pds_core/fw.c |  5 +--
 kernel/printk/braille.c                | 14 ++++----
 kernel/trace/trace.c                   |  9 +++---
 kernel/trace/trace_events.c            |  3 +-
 kernel/trace/trace_events_hist.c       | 44 ++++++++++++--------------
 kernel/trace/trace_events_synth.c      |  8 +++--
 kernel/trace/trace_stack.c             |  6 ++--
 7 files changed, 42 insertions(+), 47 deletions(-)

diff --git a/drivers/net/ethernet/amd/pds_core/fw.c b/drivers/net/ethernet/amd/pds_core/fw.c
index 7fe4594e9f9b..3f0b1137dd90 100644
--- a/drivers/net/ethernet/amd/pds_core/fw.c
+++ b/drivers/net/ethernet/amd/pds_core/fw.c
@@ -50,16 +50,13 @@ void pdsc_fw_components_invalidate(struct pdsc *pdsc)
 
 static u8 pdsc_name_to_fw_type(const char *name)
 {
-	size_t prefix_len;
 	int i;
 
 	/* "fw" without suffix maps to main firmware */
 	if (!strcmp(name, "fw"))
 		return PDS_CORE_FW_TYPE_MAIN;
 
-	prefix_len = str_has_prefix(name, PDSC_FW_COMPONENT_PREFIX);
-	if (prefix_len)
-		name += prefix_len;
+	name = strprefix(name, PDSC_FW_COMPONENT_PREFIX) ?: name;
 
 	for (i = 1; i < ARRAY_SIZE(pdsc_fw_type_names); i++) {
 		if (pdsc_fw_type_names[i] &&
diff --git a/kernel/printk/braille.c b/kernel/printk/braille.c
index 17a9591e54ff..18847d88140b 100644
--- a/kernel/printk/braille.c
+++ b/kernel/printk/braille.c
@@ -11,18 +11,18 @@
 
 int _braille_console_setup(char **str, char **brl_options)
 {
-	size_t len;
+	char *p;
 
-	len = str_has_prefix(*str, "brl,");
-	if (len) {
+	p = strprefix(*str, "brl,");
+	if (p) {
 		*brl_options = "";
-		*str += len;
+		*str = p;
 		return 0;
 	}
 
-	len = str_has_prefix(*str, "brl=");
-	if (len) {
-		*brl_options = *str + len;
+	p = strprefix(*str, "brl=");
+	if (p) {
+		*brl_options = p;
 		*str = strchr(*brl_options, ',');
 		if (!*str) {
 			pr_err("need port name after brl=\n");
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index e8c4efa8107b..1c40a00c5bd7 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -4085,19 +4085,18 @@ int set_tracer_flag(struct trace_array *tr, u64 mask, int enabled)
 
 int trace_set_options(struct trace_array *tr, char *option)
 {
-	char *cmp;
+	char *cmp, *p;
 	int neg = 0;
 	int ret;
 	size_t orig_len = strlen(option);
-	int len;
 
 	cmp = strstrip(option);
 
-	len = str_has_prefix(cmp, "no");
-	if (len)
+	p = strprefix(cmp, "no");
+	if (p)
 		neg = 1;
 
-	cmp += len;
+	cmp = p;
 
 	mutex_lock(&event_mutex);
 	mutex_lock(&trace_types_lock);
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index d467cdf9c805..72e42def1dd0 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -266,9 +266,8 @@ static struct trace_event_fields *find_event_field(const char *fmt,
 	const char *p = fmt;
 	int len;
 
-	if (!(len = str_has_prefix(fmt, "REC->")))
+	if (!(fmt = strprefix(fmt, "REC->")))
 		return NULL;
-	fmt += len;
 	for (p = fmt; *p; p++) {
 		if (!isalnum(*p) && *p != '_')
 			break;
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
index c099bfbb9fb6..d5578b67ce9f 100644
--- a/kernel/trace/trace_events_hist.c
+++ b/kernel/trace/trace_events_hist.c
@@ -1485,25 +1485,26 @@ static int parse_action(char *str, struct hist_trigger_attrs *attrs)
 static int parse_assignment(struct trace_array *tr,
 			    char *str, struct hist_trigger_attrs *attrs)
 {
-	int len, ret = 0;
+	char *p;
+	int ret = 0;
 
-	if ((len = str_has_prefix(str, "key=")) ||
-	    (len = str_has_prefix(str, "keys="))) {
-		attrs->keys_str = kstrdup(str + len, GFP_KERNEL);
+	if ((p = strprefix(str, "key=")) ||
+	    (p = strprefix(str, "keys="))) {
+		attrs->keys_str = kstrdup(p, GFP_KERNEL);
 		if (!attrs->keys_str) {
 			ret = -ENOMEM;
 			goto out;
 		}
-	} else if ((len = str_has_prefix(str, "val=")) ||
-		   (len = str_has_prefix(str, "vals=")) ||
-		   (len = str_has_prefix(str, "values="))) {
-		attrs->vals_str = kstrdup(str + len, GFP_KERNEL);
+	} else if ((p = strprefix(str, "val=")) ||
+		   (p = strprefix(str, "vals=")) ||
+		   (p = strprefix(str, "values="))) {
+		attrs->vals_str = kstrdup(p, GFP_KERNEL);
 		if (!attrs->vals_str) {
 			ret = -ENOMEM;
 			goto out;
 		}
-	} else if ((len = str_has_prefix(str, "sort="))) {
-		attrs->sort_key_str = kstrdup(str + len, GFP_KERNEL);
+	} else if ((p = strprefix(str, "sort="))) {
+		attrs->sort_key_str = kstrdup(p, GFP_KERNEL);
 		if (!attrs->sort_key_str) {
 			ret = -ENOMEM;
 			goto out;
@@ -1514,17 +1515,15 @@ static int parse_assignment(struct trace_array *tr,
 			ret = -ENOMEM;
 			goto out;
 		}
-	} else if ((len = str_has_prefix(str, "clock="))) {
-		str += len;
-
-		str = strstrip(str);
-		attrs->clock = kstrdup(str, GFP_KERNEL);
+	} else if ((p = strprefix(str, "clock="))) {
+		p = strstrip(p);
+		attrs->clock = kstrdup(p, GFP_KERNEL);
 		if (!attrs->clock) {
 			ret = -ENOMEM;
 			goto out;
 		}
-	} else if ((len = str_has_prefix(str, "size="))) {
-		int map_bits = parse_map_size(str + len);
+	} else if ((p = strprefix(str, "size="))) {
+		int map_bits = parse_map_size(p);
 
 		if (map_bits < 0) {
 			ret = map_bits;
@@ -4881,8 +4880,7 @@ static int parse_actions(struct hist_trigger_data *hist_data)
 	struct action_data *data;
 	unsigned int i;
 	int ret = 0;
-	char *str;
-	int len;
+	char *str, *p;
 
 	for (i = 0; i < hist_data->attrs->n_actions; i++) {
 		enum handler_id hid = 0;
@@ -4890,14 +4888,14 @@ static int parse_actions(struct hist_trigger_data *hist_data)
 
 		str = hist_data->attrs->action_str[i];
 
-		if ((len = str_has_prefix(str, "onmatch(")))
+		if ((p = strprefix(str, "onmatch(")))
 			hid = HANDLER_ONMATCH;
-		else if ((len = str_has_prefix(str, "onmax(")))
+		else if ((p = strprefix(str, "onmax(")))
 			hid = HANDLER_ONMAX;
-		else if ((len = str_has_prefix(str, "onchange(")))
+		else if ((p = strprefix(str, "onchange(")))
 			hid = HANDLER_ONCHANGE;
 
-		action_str = str + len;
+		action_str = p ?: str;
 
 		switch (hid) {
 		case HANDLER_ONMATCH:
diff --git a/kernel/trace/trace_events_synth.c b/kernel/trace/trace_events_synth.c
index 016ca522e5af..2a7df262aa8f 100644
--- a/kernel/trace/trace_events_synth.c
+++ b/kernel/trace/trace_events_synth.c
@@ -2268,12 +2268,14 @@ static int create_synth_event(const char *raw_command)
 
 	/* This interface accepts group name prefix */
 	if (strchr(name, '/')) {
-		len = str_has_prefix(name, SYNTH_SYSTEM "/");
-		if (len == 0) {
+		const char *n;
+
+		n = strprefix(name, SYNTH_SYSTEM "/");
+		if (!n) {
 			synth_err(SYNTH_ERR_INVALID_DYN_CMD, 0);
 			return -EINVAL;
 		}
-		name += len;
+		name = n;
 	}
 
 	len = name - raw_command;
diff --git a/kernel/trace/trace_stack.c b/kernel/trace/trace_stack.c
index e7f4e523587d..3f8ddb7b8530 100644
--- a/kernel/trace/trace_stack.c
+++ b/kernel/trace/trace_stack.c
@@ -539,10 +539,10 @@ static char stack_trace_filter_buf[COMMAND_LINE_SIZE+1] __initdata;
 
 static __init int enable_stacktrace(char *str)
 {
-	int len;
+	char *p;
 
-	if ((len = str_has_prefix(str, "_filter=")))
-		strscpy(stack_trace_filter_buf, str + len);
+	if ((p = strprefix(str, "_filter=")))
+		strscpy(stack_trace_filter_buf, p);
 
 	stack_tracer_enabled = 1;
 	return 1;
-- 
2.55.0


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

^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v6 16/19] string: Add strsuffix()
  2026-11-14 15:21 [PATCH v6 00/19] Add strprefix() and fix a bug with it Alejandro Colomar
                   ` (14 preceding siblings ...)
  2026-11-14 15:23 ` [PATCH v6 15/19] treewide: Replace some " Alejandro Colomar
@ 2026-11-14 15:23 ` Alejandro Colomar
  2026-11-14 15:24 ` [PATCH v6 17/19] string: Make strends() a legacy alias for strsuffix() Alejandro Colomar
                   ` (2 subsequent siblings)
  18 siblings, 0 replies; 37+ messages in thread
From: Alejandro Colomar @ 2026-11-14 15:23 UTC (permalink / raw)
  To: linux-hardening
  Cc: Alejandro Colomar, Krzysztof Kozlowski, Maciej W. Rozycki,
	Geert Uytterhoeven, Andreas Schwab, Andy Shevchenko,
	Bartosz Golaszewski, Daniel Scally, Danilo Krummrich,
	Greg Kroah-Hartman, Hans Verkuil, Heikki Krogerus, Joe Perches,
	Johan Hovold, Kees Cook, Laurent Pinchart, Len Brown,
	Linus Torvalds, Linus Walleij, Namhyung Kim, Rafael J. Wysocki,
	Rob Herring, Sakari Ailus, Saravana Kannan, Steven Rostedt,
	Tom Zanussi, linux-acpi, driver-core, linux-kernel, devicetree,
	linux-gpio

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

This is drop-in compatible with strends(), but is more useful, as it
gives more information.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
---
 include/linux/string.h | 28 ++++++++++++++++++++++++++--
 1 file changed, 26 insertions(+), 2 deletions(-)

diff --git a/include/linux/string.h b/include/linux/string.h
index 6b0de932e23a..2f7fb2c70662 100644
--- a/include/linux/string.h
+++ b/include/linux/string.h
@@ -47,8 +47,8 @@
 /**
  * streq - strings equal
  *
- * @s1: string to compare
- * @s2: string to compare
+ * @s1: String to compare
+ * @s2: String to compare
  *
  * Compare two strings for equality.
  *
@@ -610,6 +610,30 @@ static inline const char *strprefix(const char *s, const char *prefix)
 }
 #define strprefix(s, pfx)  const_cast(CQChar_of(s) *, strprefix(s, pfx))
 
+/**
+ * strsuffix - string suffix
+ * @s: The string to be tested
+ * @suffix: The string to see if @s ends with
+ *
+ * Returns:
+ * *  @s + strlen(@s) - strlen(@suffix) if @s ends with @suffix
+ * *  NULL if @s does not end with @suffix
+ */
+__attribute__((nonnull(1, 2)))
+static inline const char *strsuffix(const char *s, const char *suffix)
+{
+	size_t  len, sfxlen, off;
+
+	len = strlen(s);
+	sfxlen = strlen(suffix);
+	if (len < sfxlen)
+		return NULL;
+
+	off = len - sfxlen;
+	return streq(s + off, suffix) ? s + off : NULL;
+}
+#define strsuffix(s, sfx)  const_cast(CQChar_of(s) *, strsuffix(s, sfx))
+
 /**
  * str_has_prefix - Test if a string has a given prefix
  * @str: The string to test
-- 
2.55.0


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

^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v6 17/19] string: Make strends() a legacy alias for strsuffix()
  2026-11-14 15:21 [PATCH v6 00/19] Add strprefix() and fix a bug with it Alejandro Colomar
                   ` (15 preceding siblings ...)
  2026-11-14 15:23 ` [PATCH v6 16/19] string: Add strsuffix() Alejandro Colomar
@ 2026-11-14 15:24 ` Alejandro Colomar
  2026-11-14 15:24 ` [PATCH v6 18/19] treewide: s/\<strends\>/strsuffix/g Alejandro Colomar
  2026-11-14 15:24 ` [PATCH v6 19/19] string: Remove strends() Alejandro Colomar
  18 siblings, 0 replies; 37+ messages in thread
From: Alejandro Colomar @ 2026-11-14 15:24 UTC (permalink / raw)
  To: linux-hardening
  Cc: Alejandro Colomar, Krzysztof Kozlowski, Maciej W. Rozycki,
	Geert Uytterhoeven, Andreas Schwab, Andy Shevchenko,
	Bartosz Golaszewski, Daniel Scally, Danilo Krummrich,
	Greg Kroah-Hartman, Hans Verkuil, Heikki Krogerus, Joe Perches,
	Johan Hovold, Kees Cook, Laurent Pinchart, Len Brown,
	Linus Torvalds, Linus Walleij, Namhyung Kim, Rafael J. Wysocki,
	Rob Herring, Sakari Ailus, Saravana Kannan, Steven Rostedt,
	Tom Zanussi, linux-acpi, driver-core, linux-kernel, devicetree,
	linux-gpio

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

strsuffix() is drop-in compatible with strends() --but see caveats
below--, and provides more functionality for cases where we're
interested in the position of where the suffix is in the string.

strsuffix() returns a pointer that can be converted to bool.  This is
converted implicitly in most places, but if the target type is not bool,
then it might be problematic and require an explicit conversion with !!.
I checked that we don't need this anywhere, and also, I expect the
compiler would likely diagnose in such a case, so this should be safe.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
---
 include/linux/string.h | 20 +-------------------
 1 file changed, 1 insertion(+), 19 deletions(-)

diff --git a/include/linux/string.h b/include/linux/string.h
index 2f7fb2c70662..7469d4634b3e 100644
--- a/include/linux/string.h
+++ b/include/linux/string.h
@@ -633,6 +633,7 @@ static inline const char *strsuffix(const char *s, const char *suffix)
 	return streq(s + off, suffix) ? s + off : NULL;
 }
 #define strsuffix(s, sfx)  const_cast(CQChar_of(s) *, strsuffix(s, sfx))
+#define strends(s, sfx)    strsuffix(s, sfx)  // legacy alias
 
 /**
  * str_has_prefix - Test if a string has a given prefix
@@ -656,23 +657,4 @@ static __always_inline size_t str_has_prefix(const char *str, const char *prefix
 	return strncmp(str, prefix, len) == 0 ? len : 0;
 }
 
-/**
- * strends - Check if a string ends with another string.
- * @str: NULL-terminated string to check against @suffix
- * @suffix: NULL-terminated string defining the suffix to look for in @str
- *
- * Returns:
- * True if @str ends with @suffix. False in all other cases.
- */
-static inline bool __attribute__((nonnull(1, 2)))
-strends(const char *str, const char *suffix)
-{
-	unsigned int str_len = strlen(str), suffix_len = strlen(suffix);
-
-	if (str_len < suffix_len)
-		return false;
-
-	return !(strcmp(str + str_len - suffix_len, suffix));
-}
-
 #endif /* _LINUX_STRING_H_ */
-- 
2.55.0


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

^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v6 18/19] treewide: s/\<strends\>/strsuffix/g
  2026-11-14 15:21 [PATCH v6 00/19] Add strprefix() and fix a bug with it Alejandro Colomar
                   ` (16 preceding siblings ...)
  2026-11-14 15:24 ` [PATCH v6 17/19] string: Make strends() a legacy alias for strsuffix() Alejandro Colomar
@ 2026-11-14 15:24 ` Alejandro Colomar
  2026-11-14 15:24 ` [PATCH v6 19/19] string: Remove strends() Alejandro Colomar
  18 siblings, 0 replies; 37+ messages in thread
From: Alejandro Colomar @ 2026-11-14 15:24 UTC (permalink / raw)
  To: linux-hardening
  Cc: Alejandro Colomar, Krzysztof Kozlowski, Maciej W. Rozycki,
	Geert Uytterhoeven, Andreas Schwab, Andy Shevchenko,
	Bartosz Golaszewski, Daniel Scally, Danilo Krummrich,
	Greg Kroah-Hartman, Hans Verkuil, Heikki Krogerus, Joe Perches,
	Johan Hovold, Kees Cook, Laurent Pinchart, Len Brown,
	Linus Torvalds, Linus Walleij, Namhyung Kim, Rafael J. Wysocki,
	Rob Herring, Sakari Ailus, Saravana Kannan, Steven Rostedt,
	Tom Zanussi, linux-acpi, driver-core, linux-kernel, devicetree,
	linux-gpio

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

Except for the changes to "lib/tests/string_kunit.c", this was
a scripted change:

	$ git ls-files \
	| grep '\.[ch]$' \
	| grep -v -e ^tools/ -e ^scripts/ -e ^include/ \
	| xargs grep -l strends \
	| xargs sed -i 's/strends/strsuffix/g';

Signed-off-by: Alejandro Colomar <alx@kernel.org>
---
 drivers/gpio/gpiolib-shared.c |  8 ++++----
 drivers/of/property.c         |  4 ++--
 lib/tests/string_kunit.c      | 20 ++++++++++----------
 3 files changed, 16 insertions(+), 16 deletions(-)

diff --git a/drivers/gpio/gpiolib-shared.c b/drivers/gpio/gpiolib-shared.c
index 8c9ac9d93304..677ec27e4e34 100644
--- a/drivers/gpio/gpiolib-shared.c
+++ b/drivers/gpio/gpiolib-shared.c
@@ -182,8 +182,8 @@ static int gpio_shared_of_traverse(struct device_node *curr)
 		 * them. We can always just export the quirk list and
 		 * iterate over it here.
 		 */
-		if (!strends(prop->name, "-gpios") &&
-		    !strends(prop->name, "-gpio") &&
+		if (!strsuffix(prop->name, "-gpios") &&
+		    !strsuffix(prop->name, "-gpio") &&
 		    strcmp(prop->name, "gpios") != 0 &&
 		    strcmp(prop->name, "gpio") != 0)
 			continue;
@@ -240,9 +240,9 @@ static int gpio_shared_of_traverse(struct device_node *curr)
 				list_add_tail(&entry->list, &gpio_shared_list);
 			}
 
-			if (strends(prop->name, "gpios"))
+			if (strsuffix(prop->name, "gpios"))
 				suffix = "-gpios";
-			else if (strends(prop->name, "gpio"))
+			else if (strsuffix(prop->name, "gpio"))
 				suffix = "-gpio";
 			else
 				suffix = NULL;
diff --git a/drivers/of/property.c b/drivers/of/property.c
index f0729619a2a0..3a272775a8b7 100644
--- a/drivers/of/property.c
+++ b/drivers/of/property.c
@@ -1344,7 +1344,7 @@ static struct device_node *parse_suffix_prop_cells(struct device_node *np,
 {
 	struct of_phandle_args sup_args;
 
-	if (!strends(prop_name, suffix))
+	if (!strsuffix(prop_name, suffix))
 		return NULL;
 
 	if (of_parse_phandle_with_args(np, prop_name, cells_name, index,
@@ -1433,7 +1433,7 @@ static struct device_node *parse_pinctrl_n(struct device_node *np,
 static struct device_node *parse_gpios(struct device_node *np,
 				       const char *prop_name, int index)
 {
-	if (strends(prop_name, ",nr-gpios"))
+	if (strsuffix(prop_name, ",nr-gpios"))
 		return NULL;
 
 	return parse_suffix_prop_cells(np, prop_name, index, "-gpios",
diff --git a/lib/tests/string_kunit.c b/lib/tests/string_kunit.c
index f603e89866d1..f07a9480cfb1 100644
--- a/lib/tests/string_kunit.c
+++ b/lib/tests/string_kunit.c
@@ -712,16 +712,16 @@ static void string_test_memtostr(struct kunit *test)
 	KUNIT_EXPECT_EQ(test, dest[7], '\0');
 }
 
-static void string_test_strends(struct kunit *test)
+static void string_test_strsuffix(struct kunit *test)
 {
-	KUNIT_EXPECT_TRUE(test, strends("foo-bar", "bar"));
-	KUNIT_EXPECT_TRUE(test, strends("foo-bar", "-bar"));
-	KUNIT_EXPECT_TRUE(test, strends("foobar", "foobar"));
-	KUNIT_EXPECT_TRUE(test, strends("foobar", ""));
-	KUNIT_EXPECT_FALSE(test, strends("bar", "foobar"));
-	KUNIT_EXPECT_FALSE(test, strends("", "foo"));
-	KUNIT_EXPECT_FALSE(test, strends("foobar", "ba"));
-	KUNIT_EXPECT_TRUE(test, strends("", ""));
+	KUNIT_EXPECT_TRUE(test, streq(strsuffix("foo-bar", "bar"), "bar"));
+	KUNIT_EXPECT_TRUE(test, streq(strsuffix("foo-bar", "-bar"), "-bar"));
+	KUNIT_EXPECT_TRUE(test, streq(strsuffix("foobar", "foobar"), "foobar"));
+	KUNIT_EXPECT_TRUE(test, streq(strsuffix("foobar", ""), ""));
+	KUNIT_EXPECT_NULL(test, strsuffix("bar", "foobar"));
+	KUNIT_EXPECT_NULL(test, strsuffix("", "foo"));
+	KUNIT_EXPECT_NULL(test, strsuffix("foobar", "ba"));
+	KUNIT_EXPECT_TRUE(test, streq(strsuffix("", ""), ""));
 }
 
 #if IS_ENABLED(CONFIG_STRING_KUNIT_BENCH)
@@ -914,7 +914,7 @@ static struct kunit_case string_test_cases[] = {
 	KUNIT_CASE(string_test_strlcat),
 	KUNIT_CASE(string_test_strtomem),
 	KUNIT_CASE(string_test_memtostr),
-	KUNIT_CASE(string_test_strends),
+	KUNIT_CASE(string_test_strsuffix),
 	KUNIT_CASE(string_bench_strlen),
 	KUNIT_CASE(string_bench_strnlen),
 	KUNIT_CASE(string_bench_strchr),
-- 
2.55.0


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

^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v6 19/19] string: Remove strends()
  2026-11-14 15:21 [PATCH v6 00/19] Add strprefix() and fix a bug with it Alejandro Colomar
                   ` (17 preceding siblings ...)
  2026-11-14 15:24 ` [PATCH v6 18/19] treewide: s/\<strends\>/strsuffix/g Alejandro Colomar
@ 2026-11-14 15:24 ` Alejandro Colomar
  18 siblings, 0 replies; 37+ messages in thread
From: Alejandro Colomar @ 2026-11-14 15:24 UTC (permalink / raw)
  To: linux-hardening
  Cc: Alejandro Colomar, Krzysztof Kozlowski, Maciej W. Rozycki,
	Geert Uytterhoeven, Andreas Schwab, Andy Shevchenko,
	Bartosz Golaszewski, Daniel Scally, Danilo Krummrich,
	Greg Kroah-Hartman, Hans Verkuil, Heikki Krogerus, Joe Perches,
	Johan Hovold, Kees Cook, Laurent Pinchart, Len Brown,
	Linus Torvalds, Linus Walleij, Namhyung Kim, Rafael J. Wysocki,
	Rob Herring, Sakari Ailus, Saravana Kannan, Steven Rostedt,
	Tom Zanussi, linux-acpi, driver-core, linux-kernel, devicetree,
	linux-gpio

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

It's now unused, as it's been fully replaced by strsuffix().

Signed-off-by: Alejandro Colomar <alx@kernel.org>
---
 include/linux/string.h | 1 -
 1 file changed, 1 deletion(-)

diff --git a/include/linux/string.h b/include/linux/string.h
index 7469d4634b3e..f41f30f8bac7 100644
--- a/include/linux/string.h
+++ b/include/linux/string.h
@@ -633,7 +633,6 @@ static inline const char *strsuffix(const char *s, const char *suffix)
 	return streq(s + off, suffix) ? s + off : NULL;
 }
 #define strsuffix(s, sfx)  const_cast(CQChar_of(s) *, strsuffix(s, sfx))
-#define strends(s, sfx)    strsuffix(s, sfx)  // legacy alias
 
 /**
  * str_has_prefix - Test if a string has a given prefix
-- 
2.55.0


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

^ permalink raw reply related	[flat|nested] 37+ messages in thread

* Re: [PATCH v6 02/19] string: Add strprefix()
  2026-11-14 15:22 ` [PATCH v6 02/19] string: Add strprefix() Alejandro Colomar
@ 2026-11-14 15:28   ` Joe Perches
  2026-11-14 17:03     ` Alejandro Colomar
  2026-11-14 15:53   ` Joe Perches
  1 sibling, 1 reply; 37+ messages in thread
From: Joe Perches @ 2026-11-14 15:28 UTC (permalink / raw)
  To: Alejandro Colomar, linux-hardening
  Cc: Krzysztof Kozlowski, Maciej W. Rozycki, Geert Uytterhoeven,
	Andreas Schwab, Andy Shevchenko, Bartosz Golaszewski,
	Daniel Scally, Danilo Krummrich, Greg Kroah-Hartman, Hans Verkuil,
	Heikki Krogerus, Johan Hovold, Kees Cook, Laurent Pinchart,
	Len Brown, Linus Torvalds, Linus Walleij, Namhyung Kim,
	Rafael J. Wysocki, Rob Herring, Sakari Ailus, Saravana Kannan,
	Steven Rostedt, Tom Zanussi, linux-acpi, driver-core,
	linux-kernel, devicetree, linux-gpio, Bartosz Golaszewski

On Sat, 2026-11-14 at 16:22 +0100, Alejandro Colomar wrote:
> 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.

I still think this description is poor.
It's neither a bug nor a flaw.
It's a design choice.

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v6 12/19] string: Remove strstarts()
  2026-11-14 15:23 ` [PATCH v6 12/19] string: Remove strstarts() Alejandro Colomar
@ 2026-11-14 15:29   ` sashiko-bot
  2026-11-14 15:45     ` Alejandro Colomar
  0 siblings, 1 reply; 37+ messages in thread
From: sashiko-bot @ 2026-11-14 15:29 UTC (permalink / raw)
  To: Alejandro Colomar; +Cc: devicetree, linux-gpio, robh, conor+dt

Thank you for your contribution! Sashiko AI review found 1 potential
issue to consider:
- [High] string: removing strstarts() breaks build in
  xe_sysctrl_mailbox_write()
--

commit e1746880c54dd32f27fc7343468741cdc4b05246
Author: Alejandro Colomar <alx@kernel.org>

string: Remove strstarts()

This commit removes the legacy strstarts() macro alias for strprefix() from
include/linux/string.h.

> diff --git a/include/linux/string.h b/include/linux/string.h
> index 0a466360dbb39..6b0de932e23a8 100644
> --- a/include/linux/string.h
> +++ b/include/linux/string.h
> @@ -609,7 +609,6 @@ static inline const char *strprefix(const char *s, const char *prefix)
>  	return strncmp(s, prefix, len) == 0 ? s + len : NULL;
>  }
>  #define strprefix(s, pfx)  const_cast(CQChar_of(s) *, strprefix(s, pfx))
> -#define strstarts(s, pfx)  strprefix(s, pfx)  // legacy alias

[Severity: High]
Does removing strstarts() break the build when CONFIG_DRM_XE is enabled?
drivers/gpu/drm/xe/xe_sysctrl_debugfs.c:xe_sysctrl_mailbox_write() still
calls strstarts(token, "timeout=") and strstarts(token, "resplen="), which
now fail with an implicit function declaration error.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1794667407.git.alx@kernel.org?part=12

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v6 09/19] string: Make strstarts() a legacy alias for strprefix()
  2026-11-14 15:22 ` [PATCH v6 09/19] string: Make strstarts() a legacy alias for strprefix() Alejandro Colomar
@ 2026-11-14 15:33   ` sashiko-bot
  2026-11-14 15:51     ` Alejandro Colomar
  0 siblings, 1 reply; 37+ messages in thread
From: sashiko-bot @ 2026-11-14 15:33 UTC (permalink / raw)
  To: Alejandro Colomar; +Cc: devicetree, linux-gpio, robh, conor+dt

Thank you for your contribution! Sashiko AI review found 1 potential
issue to consider:
- [High] string: strstarts() macro expansion shadowed by local strprefix
  variable
--

commit e4b277f22320c7ff4dbc9e8cc88221ad43eb8ba6
Author: Alejandro Colomar <alx@kernel.org>

string: Make strstarts() a legacy alias for strprefix()

This commit replaces the inline strstarts() function with a macro alias for
strprefix().

> diff --git a/include/linux/string.h b/include/linux/string.h
> index 013c6169def05..0a466360dbb39 100644
> --- a/include/linux/string.h
> +++ b/include/linux/string.h
> @@ -609,6 +609,7 @@ static inline const char *strprefix(const char *s, const char *prefix)
>  	return strncmp(s, prefix, len) == 0 ? s + len : NULL;
>  }
>  #define strprefix(s, pfx)  const_cast(CQChar_of(s) *, strprefix(s, pfx))
> +#define strstarts(s, pfx)  strprefix(s, pfx)  // legacy alias

[Severity: High]
Does expanding strstarts() to strprefix() break the build when a caller has
a local variable named strprefix?

drivers/platform/x86/hp/hp-bioscfg/spmobj-attributes.c:hp_populate_security_buffer() {
	u16 *auth = authbuf;
	char *strprefix = NULL;
	int ret = 0;

	if (strstarts(authentication, BEAM_PREFIX)) {
    ...
}

Because the strprefix() macro expands to a call to the inline function
strprefix() in the caller's scope, the local pointer variable strprefix
shadows the function identifier and breaks compilation when
CONFIG_HP_BIOSCFG is enabled.  A later patch in this series, commit
"platform/x86: hp-bioscfg: Rename local variable", renames that variable to
prefix and should be ordered before this commit to keep the series
bisectable.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1794667407.git.alx@kernel.org?part=9

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v6 15/19] treewide: Replace some uses of str_has_prefix() by strprefix()
  2026-11-14 15:23 ` [PATCH v6 15/19] treewide: Replace some " Alejandro Colomar
@ 2026-11-14 15:34   ` sashiko-bot
  2026-11-14 15:47     ` Alejandro Colomar
  0 siblings, 1 reply; 37+ messages in thread
From: sashiko-bot @ 2026-11-14 15:34 UTC (permalink / raw)
  To: Alejandro Colomar; +Cc: devicetree, linux-gpio, robh, conor+dt

Thank you for your contribution! Sashiko AI review found 1 potential
issue to consider:
- [High] tracing: NULL pointer dereference in trace_set_options()
--

commit c09c1b839b46a18b156e43281c4fb679a76cfe40
Author: Alejandro Colomar <alx@kernel.org>

treewide: Replace some uses of str_has_prefix() by strprefix()

This commit replaces several callers of str_has_prefix() across the tree
with strprefix() to avoid manual pointer arithmetic.

> diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
> index 4ccdefb5c3efe..f9f3faaede094 100644
> --- a/kernel/trace/trace.c
> +++ b/kernel/trace/trace.c
> @@ -4085,19 +4085,18 @@ int set_tracer_flag(struct trace_array *tr, u64 mask, int enabled)
>  
>  int trace_set_options(struct trace_array *tr, char *option)
>  {
> -	char *cmp;
> +	char *cmp, *p;
>  	int neg = 0;
>  	int ret;
>  	size_t orig_len = strlen(option);
> -	int len;
>  
>  	cmp = strstrip(option);
>  
> -	len = str_has_prefix(cmp, "no");
> -	if (len)
> +	p = strprefix(cmp, "no");
> +	if (p)
>  		neg = 1;
>  
> -	cmp += len;
> +	cmp = p;

[Severity: High]
Does this set cmp to NULL when the option does not start with "no"?
strprefix() returns NULL on a non-match, and "cmp = p;" runs outside the
"if (p)" check, causing a NULL pointer dereference when cmp is passed to
match_string() right below.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1794667407.git.alx@kernel.org?part=15

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v6 12/19] string: Remove strstarts()
  2026-11-14 15:29   ` sashiko-bot
@ 2026-11-14 15:45     ` Alejandro Colomar
  0 siblings, 0 replies; 37+ messages in thread
From: Alejandro Colomar @ 2026-11-14 15:45 UTC (permalink / raw)
  To: sashiko-reviews, linux-hardening; +Cc: devicetree, linux-gpio, robh, conor+dt

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

> Date: 2026-11-14 15:29:50+0000
> From: sashiko-bot@kernel.org
>
> Thank you for your contribution! Sashiko AI review found 1 potential
> issue to consider:
> - [High] string: removing strstarts() breaks build in
>   xe_sysctrl_mailbox_write()
> --
> 
> commit e1746880c54dd32f27fc7343468741cdc4b05246
> Author: Alejandro Colomar <alx@kernel.org>
> 
> string: Remove strstarts()
> 
> This commit removes the legacy strstarts() macro alias for strprefix() from
> include/linux/string.h.
> 
> > diff --git a/include/linux/string.h b/include/linux/string.h
> > index 0a466360dbb39..6b0de932e23a8 100644
> > --- a/include/linux/string.h
> > +++ b/include/linux/string.h
> > @@ -609,7 +609,6 @@ static inline const char *strprefix(const char *s, const char *prefix)
> >  	return strncmp(s, prefix, len) == 0 ? s + len : NULL;
> >  }
> >  #define strprefix(s, pfx)  const_cast(CQChar_of(s) *, strprefix(s, pfx))
> > -#define strstarts(s, pfx)  strprefix(s, pfx)  // legacy alias
> 
> [Severity: High]
> Does removing strstarts() break the build when CONFIG_DRM_XE is enabled?
> drivers/gpu/drm/xe/xe_sysctrl_debugfs.c:xe_sysctrl_mailbox_write() still

What?!  I don't see this file or function.

	$ ls drivers/gpu/drm/xe/xe_sysctrl_debugfs.c;
	ls: cannot access 'drivers/gpu/drm/xe/xe_sysctrl_debugfs.c': No such file or directory
	$ git ls-files | grep '\.[ch]$' | xargs grep xe_sysctrl_mailbox_write;
	$

> calls strstarts(token, "timeout=") and strstarts(token, "resplen="), which
> now fail with an implicit function declaration error.
> 
> -- 
> Sashiko AI review · https://sashiko.dev/#/patchset/cover.1794667407.git.alx@kernel.org?part=12

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

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

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v6 15/19] treewide: Replace some uses of str_has_prefix() by strprefix()
  2026-11-14 15:34   ` sashiko-bot
@ 2026-11-14 15:47     ` Alejandro Colomar
  0 siblings, 0 replies; 37+ messages in thread
From: Alejandro Colomar @ 2026-11-14 15:47 UTC (permalink / raw)
  To: sashiko-reviews, linux-hardening; +Cc: devicetree, linux-gpio, robh, conor+dt

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

> Date: 2026-11-14 15:34:40+0000
> From: sashiko-bot@kernel.org
>
> Thank you for your contribution! Sashiko AI review found 1 potential
> issue to consider:
> - [High] tracing: NULL pointer dereference in trace_set_options()
> --
> 
> commit c09c1b839b46a18b156e43281c4fb679a76cfe40
> Author: Alejandro Colomar <alx@kernel.org>
> 
> treewide: Replace some uses of str_has_prefix() by strprefix()
> 
> This commit replaces several callers of str_has_prefix() across the tree
> with strprefix() to avoid manual pointer arithmetic.
> 
> > diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
> > index 4ccdefb5c3efe..f9f3faaede094 100644
> > --- a/kernel/trace/trace.c
> > +++ b/kernel/trace/trace.c
> > @@ -4085,19 +4085,18 @@ int set_tracer_flag(struct trace_array *tr, u64 mask, int enabled)
> >  
> >  int trace_set_options(struct trace_array *tr, char *option)
> >  {
> > -	char *cmp;
> > +	char *cmp, *p;
> >  	int neg = 0;
> >  	int ret;
> >  	size_t orig_len = strlen(option);
> > -	int len;
> >  
> >  	cmp = strstrip(option);
> >  
> > -	len = str_has_prefix(cmp, "no");
> > -	if (len)
> > +	p = strprefix(cmp, "no");
> > +	if (p)
> >  		neg = 1;
> >  
> > -	cmp += len;
> > +	cmp = p;

Ouch!  I meant to write 'cmp = p ?: cmp;'.

Will fix for v7.

> 
> [Severity: High]
> Does this set cmp to NULL when the option does not start with "no"?
> strprefix() returns NULL on a non-match, and "cmp = p;" runs outside the
> "if (p)" check, causing a NULL pointer dereference when cmp is passed to
> match_string() right below.
> 
> -- 
> Sashiko AI review · https://sashiko.dev/#/patchset/cover.1794667407.git.alx@kernel.org?part=15

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

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

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v6 09/19] string: Make strstarts() a legacy alias for strprefix()
  2026-11-14 15:33   ` sashiko-bot
@ 2026-11-14 15:51     ` Alejandro Colomar
  2026-11-14 17:52       ` Alejandro Colomar
  0 siblings, 1 reply; 37+ messages in thread
From: Alejandro Colomar @ 2026-11-14 15:51 UTC (permalink / raw)
  To: sashiko-reviews, linux-hardening; +Cc: devicetree, linux-gpio, robh, conor+dt

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

> Date: 2026-11-14 15:33:19+0000
> From: sashiko-bot@kernel.org
>
> Thank you for your contribution! Sashiko AI review found 1 potential
> issue to consider:
> - [High] string: strstarts() macro expansion shadowed by local strprefix
>   variable
> --
> 
> commit e4b277f22320c7ff4dbc9e8cc88221ad43eb8ba6
> Author: Alejandro Colomar <alx@kernel.org>
> 
> string: Make strstarts() a legacy alias for strprefix()
> 
> This commit replaces the inline strstarts() function with a macro alias for
> strprefix().
> 
> > diff --git a/include/linux/string.h b/include/linux/string.h
> > index 013c6169def05..0a466360dbb39 100644
> > --- a/include/linux/string.h
> > +++ b/include/linux/string.h
> > @@ -609,6 +609,7 @@ static inline const char *strprefix(const char *s, const char *prefix)
> >  	return strncmp(s, prefix, len) == 0 ? s + len : NULL;
> >  }
> >  #define strprefix(s, pfx)  const_cast(CQChar_of(s) *, strprefix(s, pfx))
> > +#define strstarts(s, pfx)  strprefix(s, pfx)  // legacy alias
> 
> [Severity: High]
> Does expanding strstarts() to strprefix() break the build when a caller has
> a local variable named strprefix?
> 
> drivers/platform/x86/hp/hp-bioscfg/spmobj-attributes.c:hp_populate_security_buffer() {
> 	u16 *auth = authbuf;
> 	char *strprefix = NULL;
> 	int ret = 0;
> 
> 	if (strstarts(authentication, BEAM_PREFIX)) {
>     ...
> }
> 
> Because the strprefix() macro expands to a call to the inline function
> strprefix() in the caller's scope, the local pointer variable strprefix
> shadows the function identifier and breaks compilation when
> CONFIG_HP_BIOSCFG is enabled.  A later patch in this series, commit
> "platform/x86: hp-bioscfg: Rename local variable", renames that variable to
> prefix and should be ordered before this commit to keep the series
> bisectable.

This reivew is wrong.  A local variable cannot shadow a macro.  Also,
a function-like macro is only expanded if it's followed by an opening
parenthesis.  In the case of spmobj-attributes.c, the local variable
doesn't have a parenthesis, and thus doesn't trigger expansion.

> 
> -- 
> Sashiko AI review · https://sashiko.dev/#/patchset/cover.1794667407.git.alx@kernel.org?part=9

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

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

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v6 02/19] string: Add strprefix()
  2026-11-14 15:22 ` [PATCH v6 02/19] string: Add strprefix() Alejandro Colomar
  2026-11-14 15:28   ` Joe Perches
@ 2026-11-14 15:53   ` Joe Perches
  2026-11-14 16:59     ` Alejandro Colomar
  2026-11-14 19:49     ` Off-by-one in arch/mips/bcm47xx/time.c (was: [PATCH v6 02/19] string: Add strprefix()) Alejandro Colomar
  1 sibling, 2 replies; 37+ messages in thread
From: Joe Perches @ 2026-11-14 15:53 UTC (permalink / raw)
  To: Alejandro Colomar, linux-hardening
  Cc: Krzysztof Kozlowski, Maciej W. Rozycki, Geert Uytterhoeven,
	Andreas Schwab, Andy Shevchenko, Bartosz Golaszewski,
	Daniel Scally, Danilo Krummrich, Greg Kroah-Hartman, Hans Verkuil,
	Heikki Krogerus, Johan Hovold, Kees Cook, Laurent Pinchart,
	Len Brown, Linus Torvalds, Linus Walleij, Namhyung Kim,
	Rafael J. Wysocki, Rob Herring, Sakari Ailus, Saravana Kannan,
	Steven Rostedt, Tom Zanussi, linux-acpi, driver-core,
	linux-kernel, devicetree, linux-gpio, Bartosz Golaszewski

On Sat, 2026-11-14 at 16:22 +0100, Alejandro Colomar wrote:
> 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().
> 
> Move the description from str_has_prefix(), and describe
> str_has_prefix() by comparing it to strprefix().
> 
> Closes: https://lore.kernel.org/linux-hardening/20261104-fwnode-for-each-prop-v1-1-6e28fe1c72c4@oss.qualcomm.com/
> Reported-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
> Signed-off-by: Alejandro Colomar <alx@kernel.org>
> ---
>  include/linux/string.h   | 29 ++++++++++++++++++++++++-----
>  lib/tests/string_kunit.c |  9 +++++++++
>  2 files changed, 33 insertions(+), 5 deletions(-)
> 
> diff --git a/include/linux/string.h b/include/linux/string.h
> index 9b3e578d9aca..14e825e902eb 100644
> --- a/include/linux/string.h
> +++ b/include/linux/string.h
> @@ -557,16 +557,35 @@ void memcpy_and_pad(void *dest, size_t dest_len, const void *src, size_t count,
>  	       sizeof(*(obj)) - offsetof(typeof(*(obj)), member));	\
>  })
>  
> +/**
> + * 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(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
> + * *  NULL if @s does not start with @prefix
> + */
> +__attribute__((nonnull(1, 2)))

__nonnull_args(1, 2)

A negative of this is that it implies the return should
always be tested even though it can be known that the
return is effectively bool.

Also the block

> + * A common way to test a prefix of a string is to do:
> + *  strncmp(s, prefix, sizeof(prefix) - 1)

IMO isn't useful and the description doesn't say what happens
when prefix is an empty string (which IMO should match).

And the name strprefix could be confused with a function
that actually prefixes a string to another string.

maybe strprefixed or str_is_prefixed ?

And these are all candidates for converted uses to
whatever is the function name or steq:

$ git grep -P '[^w]strncmp\s*\(\s*[\w\.\-\>\s]*,\s*\"\w*"\s*,\s*\d+\s*\)' -- '*.[ch]' | wc -l
946

[^w] because of prom_strncmp

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v6 10/19] platform/x86: hp-bioscfg: Rename local variable
  2026-11-14 15:23 ` [PATCH v6 10/19] platform/x86: hp-bioscfg: Rename local variable Alejandro Colomar
@ 2026-11-14 16:04   ` Joe Perches
  2026-11-14 17:08     ` Alejandro Colomar
  0 siblings, 1 reply; 37+ messages in thread
From: Joe Perches @ 2026-11-14 16:04 UTC (permalink / raw)
  To: Alejandro Colomar, linux-hardening
  Cc: Krzysztof Kozlowski, Maciej W. Rozycki, Geert Uytterhoeven,
	Andreas Schwab, Andy Shevchenko, Bartosz Golaszewski,
	Daniel Scally, Danilo Krummrich, Greg Kroah-Hartman, Hans Verkuil,
	Heikki Krogerus, Johan Hovold, Kees Cook, Laurent Pinchart,
	Len Brown, Linus Torvalds, Linus Walleij, Namhyung Kim,
	Rafael J. Wysocki, Rob Herring, Sakari Ailus, Saravana Kannan,
	Steven Rostedt, Tom Zanussi, linux-acpi, driver-core,
	linux-kernel, devicetree, linux-gpio

On Sat, 2026-11-14 at 16:23 +0100, Alejandro Colomar wrote:
> strprefix shadows a function of the same name.  Let's rename it.
[]
> diff --git a/drivers/platform/x86/hp/hp-bioscfg/spmobj-attributes.c b/drivers/platform/x86/hp/hp-bioscfg/spmobj-attributes.c
[]
> @@ -87,13 +87,13 @@ int hp_populate_security_buffer(u16 *authbuf, const char *authentication)
>  		 */
>  
>  		/* append UTF_PREFIX to part and then convert it to unicode */
> -		strprefix = kasprintf(GFP_KERNEL, "%s%s", UTF_PREFIX,
> +		prefix = kasprintf(GFP_KERNEL, "%s%s", UTF_PREFIX,
>  				      authentication);
> -		if (!strprefix)
> +		if (!prefix)
>  			return -ENOMEM;
>  
> -		auth = hp_ascii_to_utf16_unicode(auth, strprefix);
> -		kfree(strprefix);
> +		auth = hp_ascii_to_utf16_unicode(auth, prefix);

unassociated defect but the auth buffer isn't apparently sized
so some write beyond buffer seems possible.

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v6 02/19] string: Add strprefix()
  2026-11-14 15:53   ` Joe Perches
@ 2026-11-14 16:59     ` Alejandro Colomar
  2026-11-14 18:24       ` Joe Perches
  2026-11-14 19:49     ` Off-by-one in arch/mips/bcm47xx/time.c (was: [PATCH v6 02/19] string: Add strprefix()) Alejandro Colomar
  1 sibling, 1 reply; 37+ messages in thread
From: Alejandro Colomar @ 2026-11-14 16:59 UTC (permalink / raw)
  To: Joe Perches
  Cc: Alejandro Colomar, linux-hardening, Krzysztof Kozlowski,
	Maciej W. Rozycki, Geert Uytterhoeven, Andreas Schwab,
	Andy Shevchenko, Bartosz Golaszewski, Daniel Scally,
	Danilo Krummrich, Greg Kroah-Hartman, Hans Verkuil,
	Heikki Krogerus, Johan Hovold, Kees Cook, Laurent Pinchart,
	Len Brown, Linus Torvalds, Linus Walleij, Namhyung Kim,
	Rafael J. Wysocki, Rob Herring, Sakari Ailus, Saravana Kannan,
	Steven Rostedt, Tom Zanussi, linux-acpi, driver-core,
	linux-kernel, devicetree, linux-gpio, Bartosz Golaszewski

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

Hi Joe,

> Date: 2026-11-14 07:53:25-0800
> From: Joe Perches <joe@perches.com>
>
[...]
> > +/**
> > + * 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(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
> > + * *  NULL if @s does not start with @prefix
> > + */
> > +__attribute__((nonnull(1, 2)))
> 
> __nonnull_args(1, 2)

Thanks!

> A negative of this is that it implies the return should
> always be tested even though it can be known that the
> return is effectively bool.

I'm not sure I follow.  The attribute is about the arguments, not the
return value.  Perhaps I don't understand what you mean by "this".

> 
> Also the block
> 
> > + * A common way to test a prefix of a string is to do:
> > + *  strncmp(s, prefix, sizeof(prefix) - 1)
> 
> IMO isn't useful

I tend to agree.  I think that is just one example of code this API
replaces, but there are many more.  I think I'd prefer a description of
the API, without comparing it to existing patterns (because there might
be too many).

> and the description doesn't say what happens
> when prefix is an empty string (which IMO should match).

It doesn't, because the general behavior naturally extends to that
extreme.  That is, it's the non-surprising result.

For any non-empty prefix, the function succeeds if all the non-null
characters from the prefix are found sequentially at the begining of the
string.  In other words, it matches if and only if strstr(s,prefix)==s.

For the empty prefix, it still behaves like that.  If all the non-null
characters from the prefix are found sequentially at the begining of the
string, it succeeds.  This always happens, of course, so strprefix(s,"")
always succeeds.  Similarly, strstr(s,"")==s.

Since there's nothing different, I don't see a strong reason to document
it at all.  And that's also the reason why I think the issue in
str_has_prefix() is not just a different design decision, but rather
a bug.  If the name of str_has_prefix() had been strprefix_len() --which
BTW, I'm tempted to rename in this patch set--, then it would be just a
design decision, as it would behave correctly for what the name implies,
and it would be an incorrect use if one tries to use it for determining
whether a strings starts with a possibly-empty prefix or not.  But the
function is called str_has_prefix(), which implies a boolean behavior,
and that boolean behavior is wrong for the case of the empty string.
That is, while any string starts with an empty string, str_has_prefix()
returns false, and that is wrong.

> And the name strprefix could be confused with a function
> that actually prefixes a string to another string.

This would be a stretch.  <string.h> functions, with the exception of
the *dup() functions, do not allocate memory.  They're a collection of
functions that only perform byte operations on strings, or in general,
on character arrays.

Prefixing a string to another string would require either knowing the
size of the buffer (and thus have a size_t parameter), or allocate new
memory.  Since this function doesn't have a size_t, the former can be
ruled out.  And allocating memory would be very weird, so we can also
discard that.

> maybe strprefixed or str_is_prefixed ?

strprefixed() might be okay-ish.  I still prefer strprefix(), because
common sense can rule those interpretations out, and also because it's
widely implemented with that name.

> And these are all candidates for converted uses to
> whatever is the function name or steq:
> 
> $ git grep -P '[^w]strncmp\s*\(\s*[\w\.\-\>\s]*,\s*\"\w*"\s*,\s*\d+\s*\)' -- '*.[ch]' | wc -l
> 946

Yup, there's a lot of code that could be converted.  For now, I find it
more pressing to transform the existing calls to strstarts(), and as
many of str_has_prefix() as reasonable, to minimize drift, but I can
also work later on converting the strncmp(3) calls.

> [^w] because of prom_strncmp

Since there's no anchor before the 's', that doesn't change anything:

$ git grep -P '[^w]strncmp\s*\(\s*[\w\.\-\>\s]*,\s*\"\w*"\s*,\s*\d+\s*\)' -- '*.[ch]' | wc -l
946
$ git grep -P 'strncmp\s*\(\s*[\w\.\-\>\s]*,\s*\"\w*"\s*,\s*\d+\s*\)' -- '*.[ch]' | wc -l
946

> 

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

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

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v6 02/19] string: Add strprefix()
  2026-11-14 15:28   ` Joe Perches
@ 2026-11-14 17:03     ` Alejandro Colomar
  2026-11-14 18:35       ` Alejandro Colomar
  0 siblings, 1 reply; 37+ messages in thread
From: Alejandro Colomar @ 2026-11-14 17:03 UTC (permalink / raw)
  To: Joe Perches
  Cc: Alejandro Colomar, linux-hardening, Krzysztof Kozlowski,
	Maciej W. Rozycki, Geert Uytterhoeven, Andreas Schwab,
	Andy Shevchenko, Bartosz Golaszewski, Daniel Scally,
	Danilo Krummrich, Greg Kroah-Hartman, Hans Verkuil,
	Heikki Krogerus, Johan Hovold, Kees Cook, Laurent Pinchart,
	Len Brown, Linus Torvalds, Linus Walleij, Namhyung Kim,
	Rafael J. Wysocki, Rob Herring, Sakari Ailus, Saravana Kannan,
	Steven Rostedt, Tom Zanussi, linux-acpi, driver-core,
	linux-kernel, devicetree, linux-gpio, Bartosz Golaszewski

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

Hi Joe,

> Date: 2026-11-14 07:28:23-0800
> From: Joe Perches <joe@perches.com>
>
> On Sat, 2026-11-14 at 16:22 +0100, Alejandro Colomar wrote:
> > 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.
> 
> I still think this description is poor.
> It's neither a bug nor a flaw.
> It's a design choice.

As explained in more detail in the subthread, I think it is a bug.

If the function name were strprefix_len(), it would be a legitimate
design choice, since 0 would indeed be the length of such a prefix, and
it would be arguably useful to return 0 on a non-matching prefix.

But the function is named str_has_prefix().  Since an empty string is by
convention (strstr(3), etc.) the prefix of every string, returning false
is a bug.  Since the function returns 0 for such a case, it misbehaves.

Perhaps we can reach an agreement if I prepend this commit with one that
renames all uses of str_has_prefix() to strprefix_len().  Then, I would
agree to consider this as a design choice, and remove the text above.


Cheers,
Alex

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

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

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v6 10/19] platform/x86: hp-bioscfg: Rename local variable
  2026-11-14 16:04   ` Joe Perches
@ 2026-11-14 17:08     ` Alejandro Colomar
  0 siblings, 0 replies; 37+ messages in thread
From: Alejandro Colomar @ 2026-11-14 17:08 UTC (permalink / raw)
  To: Joe Perches
  Cc: linux-hardening, Krzysztof Kozlowski, Maciej W. Rozycki,
	Geert Uytterhoeven, Andreas Schwab, Andy Shevchenko,
	Bartosz Golaszewski, Daniel Scally, Danilo Krummrich,
	Greg Kroah-Hartman, Hans Verkuil, Heikki Krogerus, Johan Hovold,
	Kees Cook, Laurent Pinchart, Len Brown, Linus Torvalds,
	Linus Walleij, Namhyung Kim, Rafael J. Wysocki, Rob Herring,
	Sakari Ailus, Saravana Kannan, Steven Rostedt, Tom Zanussi,
	linux-acpi, driver-core, linux-kernel, devicetree, linux-gpio

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

Hi Joe,

> Date: 2026-11-14 08:04:56-0800
> From: Joe Perches <joe@perches.com>
>
> On Sat, 2026-11-14 at 16:23 +0100, Alejandro Colomar wrote:
> > strprefix shadows a function of the same name.  Let's rename it.
> []
> > diff --git a/drivers/platform/x86/hp/hp-bioscfg/spmobj-attributes.c b/drivers/platform/x86/hp/hp-bioscfg/spmobj-attributes.c
> []
> > @@ -87,13 +87,13 @@ int hp_populate_security_buffer(u16 *authbuf, const char *authentication)
> >  		 */
> >  
> >  		/* append UTF_PREFIX to part and then convert it to unicode */
> > -		strprefix = kasprintf(GFP_KERNEL, "%s%s", UTF_PREFIX,
> > +		prefix = kasprintf(GFP_KERNEL, "%s%s", UTF_PREFIX,
> >  				      authentication);
> > -		if (!strprefix)
> > +		if (!prefix)
> >  			return -ENOMEM;
> >  
> > -		auth = hp_ascii_to_utf16_unicode(auth, strprefix);
> > -		kfree(strprefix);
> > +		auth = hp_ascii_to_utf16_unicode(auth, prefix);
> 
> unassociated defect but the auth buffer isn't apparently sized
> so some write beyond buffer seems possible.

Thanks!

I don't fully understand that utf code, so I'll leave this to anyone
else who knows about it.  But I'm interested in it, so if anyone writes
a patch, please CC me.


Cheers,
Alex

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

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

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v6 09/19] string: Make strstarts() a legacy alias for strprefix()
  2026-11-14 15:51     ` Alejandro Colomar
@ 2026-11-14 17:52       ` Alejandro Colomar
  0 siblings, 0 replies; 37+ messages in thread
From: Alejandro Colomar @ 2026-11-14 17:52 UTC (permalink / raw)
  To: Alejandro Colomar
  Cc: sashiko-reviews, linux-hardening, devicetree, linux-gpio, robh,
	conor+dt

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

> Date: 2026-11-14 16:51:58+0100
> From: Alejandro Colomar <alx@kernel.org>
>
> > Date: 2026-11-14 15:33:19+0000
> > From: sashiko-bot@kernel.org
> >
> > Thank you for your contribution! Sashiko AI review found 1 potential
> > issue to consider:
> > - [High] string: strstarts() macro expansion shadowed by local strprefix
> >   variable
> > --
> > 
> > commit e4b277f22320c7ff4dbc9e8cc88221ad43eb8ba6
> > Author: Alejandro Colomar <alx@kernel.org>
> > 
> > string: Make strstarts() a legacy alias for strprefix()
> > 
> > This commit replaces the inline strstarts() function with a macro alias for
> > strprefix().
> > 
> > > diff --git a/include/linux/string.h b/include/linux/string.h
> > > index 013c6169def05..0a466360dbb39 100644
> > > --- a/include/linux/string.h
> > > +++ b/include/linux/string.h
> > > @@ -609,6 +609,7 @@ static inline const char *strprefix(const char *s, const char *prefix)
> > >  	return strncmp(s, prefix, len) == 0 ? s + len : NULL;
> > >  }
> > >  #define strprefix(s, pfx)  const_cast(CQChar_of(s) *, strprefix(s, pfx))
> > > +#define strstarts(s, pfx)  strprefix(s, pfx)  // legacy alias
> > 
> > [Severity: High]
> > Does expanding strstarts() to strprefix() break the build when a caller has
> > a local variable named strprefix?
> > 
> > drivers/platform/x86/hp/hp-bioscfg/spmobj-attributes.c:hp_populate_security_buffer() {
> > 	u16 *auth = authbuf;
> > 	char *strprefix = NULL;
> > 	int ret = 0;
> > 
> > 	if (strstarts(authentication, BEAM_PREFIX)) {
> >     ...
> > }
> > 
> > Because the strprefix() macro expands to a call to the inline function
> > strprefix() in the caller's scope, the local pointer variable strprefix
> > shadows the function identifier and breaks compilation when
> > CONFIG_HP_BIOSCFG is enabled.  A later patch in this series, commit
> > "platform/x86: hp-bioscfg: Rename local variable", renames that variable to
> > prefix and should be ordered before this commit to keep the series
> > bisectable.
> 
> This reivew is wrong.  A local variable cannot shadow a macro.  Also,
> a function-like macro is only expanded if it's followed by an opening
> parenthesis.  In the case of spmobj-attributes.c, the local variable
> doesn't have a parenthesis, and thus doesn't trigger expansion.

After looking at it a second time, while the report from sashiko is
wrong, the code is actually wrong, and reordering the commits would be
appropriate.  I'll reorder them in v7.

> 
> > 
> > -- 
> > Sashiko AI review · https://sashiko.dev/#/patchset/cover.1794667407.git.alx@kernel.org?part=9
> 
> -- 
> <https://www.alejandro-colomar.es>



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

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

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v6 02/19] string: Add strprefix()
  2026-11-14 16:59     ` Alejandro Colomar
@ 2026-11-14 18:24       ` Joe Perches
  2026-11-14 19:00         ` Alejandro Colomar
  0 siblings, 1 reply; 37+ messages in thread
From: Joe Perches @ 2026-11-14 18:24 UTC (permalink / raw)
  To: Alejandro Colomar
  Cc: Alejandro Colomar, linux-hardening, Krzysztof Kozlowski,
	Maciej W. Rozycki, Geert Uytterhoeven, Andreas Schwab,
	Andy Shevchenko, Bartosz Golaszewski, Daniel Scally,
	Danilo Krummrich, Greg Kroah-Hartman, Hans Verkuil,
	Heikki Krogerus, Johan Hovold, Kees Cook, Laurent Pinchart,
	Len Brown, Linus Torvalds, Linus Walleij, Namhyung Kim,
	Rafael J. Wysocki, Rob Herring, Sakari Ailus, Saravana Kannan,
	Steven Rostedt, Tom Zanussi, linux-acpi, driver-core,
	linux-kernel, devicetree, linux-gpio, Bartosz Golaszewski

On Sat, 2026-11-14 at 17:59 +0100, Alejandro Colomar wrote:
> > A negative of this is that it implies the return should
> > always be tested even though it can be known that the
> > return is effectively bool.
> 
> I'm not sure I follow.  The attribute is about the arguments, not the
> return value.  Perhaps I don't understand what you mean by "this".

'this' is the return of the strprefix function itself.

It returns either a const char * or a char * or NULL.

Because it can return NULL, then the return value
could/should be tested.

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v6 02/19] string: Add strprefix()
  2026-11-14 17:03     ` Alejandro Colomar
@ 2026-11-14 18:35       ` Alejandro Colomar
  0 siblings, 0 replies; 37+ messages in thread
From: Alejandro Colomar @ 2026-11-14 18:35 UTC (permalink / raw)
  To: Joe Perches
  Cc: Alejandro Colomar, linux-hardening, Krzysztof Kozlowski,
	Maciej W. Rozycki, Geert Uytterhoeven, Andreas Schwab,
	Andy Shevchenko, Bartosz Golaszewski, Daniel Scally,
	Danilo Krummrich, Greg Kroah-Hartman, Hans Verkuil,
	Heikki Krogerus, Johan Hovold, Kees Cook, Laurent Pinchart,
	Len Brown, Linus Torvalds, Linus Walleij, Namhyung Kim,
	Rafael J. Wysocki, Rob Herring, Sakari Ailus, Saravana Kannan,
	Steven Rostedt, Tom Zanussi, linux-acpi, driver-core,
	linux-kernel, devicetree, linux-gpio, Bartosz Golaszewski,
	Rusty Russell

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

[CC += Rusty]

Hi Joe,

> Date: 2026-11-14 18:03:44+0100
> From: Alejandro Colomar <alx+linux-hardening@kernel.org>
>
> Hi Joe,
> 
> > Date: 2026-11-14 07:28:23-0800
> > From: Joe Perches <joe@perches.com>
> >
> > On Sat, 2026-11-14 at 16:22 +0100, Alejandro Colomar wrote:
> > > 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.
> > 
> > I still think this description is poor.
> > It's neither a bug nor a flaw.
> > It's a design choice.
> 
> As explained in more detail in the subthread, I think it is a bug.
> 
> If the function name were strprefix_len(), it would be a legitimate
> design choice, since 0 would indeed be the length of such a prefix, and
> it would be arguably useful to return 0 on a non-matching prefix.
> 
> But the function is named str_has_prefix().  Since an empty string is by
> convention (strstr(3), etc.) the prefix of every string, returning false
> is a bug.  Since the function returns 0 for such a case, it misbehaves.
> 
> Perhaps we can reach an agreement if I prepend this commit with one that
> renames all uses of str_has_prefix() to strprefix_len().  Then, I would
> agree to consider this as a design choice, and remove the text above.

Here's the list of categories from Rusty Russell:

	10. It's impossible to get wrong.
	9. The compiler/linker won't let you get it wrong.
	8. The compiler will warn if you get it wrong.
	7. The obvious use is (probably) the correct one.
	6. The name tells you how to use it.
	5. Do it right or it will always break at runtime.
	4. Follow common convention and you'll get it right.
	3. Read the documentation and you'll get it right.
	2. Read the implementation and you'll get it right.
	1. Read the correct mailing list thread and you'll get it right.

	-1. Read the mailing list thread and you'll get it wrong.
	-2. Read the implementation and you'll get it wrong.
	-3. Read the documentation and you'll get it wrong.
	-4. Follow common convention and you'll get it wrong.
	-5. Do it right and it will sometimes break at runtime.
	-6. The name tells you how not to use it.
	-7. The obvious use is wrong.
	-8. The compiler will warn if you get it right.
	-9. The compiler/linker won't let you get it right.
	-10. It's impossible to get right.

str_has_prefix() would get a -7, because the obvious use is wrong, or
perhaps a -6, because the name tells you how not to use it.

strprefix() is not perfect, but it's decent.  I'd say it can be a 3 at
least, if we document it as the following:

	This function determines whether the string pointed to by @s
	starts with the sequence of characters
	(excluding the terminating null character)
	in the string pointed to by @prefix.

	Returns:
	*  @s + strlen(@prefix) if @s starts with @prefix
	*  NULL if @s does not start with @prefix


Cheers,
Alex

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

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

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v6 02/19] string: Add strprefix()
  2026-11-14 18:24       ` Joe Perches
@ 2026-11-14 19:00         ` Alejandro Colomar
  0 siblings, 0 replies; 37+ messages in thread
From: Alejandro Colomar @ 2026-11-14 19:00 UTC (permalink / raw)
  To: Joe Perches
  Cc: Alejandro Colomar, linux-hardening, Krzysztof Kozlowski,
	Maciej W. Rozycki, Geert Uytterhoeven, Andreas Schwab,
	Andy Shevchenko, Bartosz Golaszewski, Daniel Scally,
	Danilo Krummrich, Greg Kroah-Hartman, Hans Verkuil,
	Heikki Krogerus, Johan Hovold, Kees Cook, Laurent Pinchart,
	Len Brown, Linus Torvalds, Linus Walleij, Namhyung Kim,
	Rafael J. Wysocki, Rob Herring, Sakari Ailus, Saravana Kannan,
	Steven Rostedt, Tom Zanussi, linux-acpi, driver-core,
	linux-kernel, devicetree, linux-gpio, Bartosz Golaszewski

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

Hi Joe,

> Date: 2026-11-14 10:24:26-0800
> From: Joe Perches <joe@perches.com>
>
> On Sat, 2026-11-14 at 17:59 +0100, Alejandro Colomar wrote:
> > > A negative of this is that it implies the return should
> > > always be tested even though it can be known that the
> > > return is effectively bool.
> > 
> > I'm not sure I follow.  The attribute is about the arguments, not the
> > return value.  Perhaps I don't understand what you mean by "this".
> 
> 'this' is the return of the strprefix function itself.
> 
> It returns either a const char * or a char * or NULL.
> 
> Because it can return NULL, then the return value
> could/should be tested.

Yes, it must be tested, when not used as a boolean.
The usual replacement of
	s += str_has_prefix(s, prefix);
would be
	s = strprefix(s, prefix) ?: s;

The boolean use remains trivial:

	-       return strstarts(name, ".init") ||
	-               strstarts(name, ".ARM.extab.init") ||
	-               strstarts(name, ".ARM.exidx.init");
	+       return strprefix(name, ".init") ||
	+               strprefix(name, ".ARM.extab.init") ||
	+               strprefix(name, ".ARM.exidx.init");
and
	-       if (!str_has_prefix(p, "console=") && !strstr(p, " console=")) {
	+       if (!strprefix(p, "console=") && !strstr(p, " console=")) {

The non-boolean use would look like this (actual diffs from the patch
set):

	-       prefix_len = str_has_prefix(name, PDSC_FW_COMPONENT_PREFIX);
	-       if (prefix_len)
	-               name += prefix_len;
	+       name = strprefix(name, PDSC_FW_COMPONENT_PREFIX) ?: name;

(the old code, actually, didn't need that test, and could have been
 simply 'name += str_has_prefix(name, PDSC_FW_COMPONENT_PREFIX);')

and
	-       len = str_has_prefix(*str, "brl,");
	-       if (len) {
	+       p = strprefix(*str, "brl,");
	+       if (p) {
			*brl_options = "";
	-               *str += len;
	+               *str = p;
			return 0;
		}

and
		cmp = strstrip(option);
	 
	-       len = str_has_prefix(cmp, "no");
	-       if (len)
	+       p = strprefix(cmp, "no");
	+       if (p) {
			neg = 1;
	-
	-       cmp += len;
	+               cmp = p;
	+       }


Have a lovely night!
Alex

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

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

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Off-by-one in arch/mips/bcm47xx/time.c (was: [PATCH v6 02/19] string: Add strprefix())
  2026-11-14 15:53   ` Joe Perches
  2026-11-14 16:59     ` Alejandro Colomar
@ 2026-11-14 19:49     ` Alejandro Colomar
  1 sibling, 0 replies; 37+ messages in thread
From: Alejandro Colomar @ 2026-11-14 19:49 UTC (permalink / raw)
  To: Hauke Mehrtens
  Cc: linux-mips, Ralf Baechle, Joe Perches, Alejandro Colomar,
	linux-hardening, Krzysztof Kozlowski, Maciej W. Rozycki,
	Geert Uytterhoeven, Andreas Schwab, Andy Shevchenko,
	Bartosz Golaszewski, Daniel Scally, Danilo Krummrich,
	Greg Kroah-Hartman, Hans Verkuil, Heikki Krogerus, Johan Hovold,
	Kees Cook, Laurent Pinchart, Len Brown, Linus Torvalds,
	Linus Walleij, Namhyung Kim, Rafael J. Wysocki, Rob Herring,
	Sakari Ailus, Saravana Kannan, Steven Rostedt, Tom Zanussi,
	linux-acpi, driver-core, linux-kernel, devicetree, linux-gpio,
	Bartosz Golaszewski

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

Hi Hauke,

> Date: 2026-11-14 07:53:25-0800
> From: Joe Perches <joe@perches.com>
>
[...]
> And these are all candidates for converted uses to
> whatever is the function name or steq:
> 
> $ git grep -P '[^w]strncmp\s*\(\s*[\w\.\-\>\s]*,\s*\"\w*"\s*,\s*\d+\s*\)' -- '*.[ch]' | wc -l
> 946

Looking at those, I've seen a bug in BCM47XX, introduced in 2224de9d152b
(2013-10-29; "MIPS: BCM47XX: Fix clock detection for BCM5354 with 200MHz
 clock").

Look at this line of code:

	$ grep strncmp arch/mips/bcm47xx/time.c
			if (len >= 0 && !strncmp(buf, "200", 4))

That strncmp(3) call is wrong, as it's really behaving as strcmp(3),
since the 4 is off-by-one.  I'd like to know what was the intention.
Was it meant to be !strncmp(buf, "200", 3)?  Or was it meant to be just
!strcmp(buf, "200")?


Have a lovely night!
Alex

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

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

^ permalink raw reply	[flat|nested] 37+ messages in thread

end of thread, other threads:[~2026-11-14 19:49 UTC | newest]

Thread overview: 37+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-14 15:21 [PATCH v6 00/19] Add strprefix() and fix a bug with it Alejandro Colomar
2026-11-14 15:21 ` [PATCH v6 01/19] string: Add streq() Alejandro Colomar
2026-11-14 15:22 ` [PATCH v6 02/19] string: Add strprefix() Alejandro Colomar
2026-11-14 15:28   ` Joe Perches
2026-11-14 17:03     ` Alejandro Colomar
2026-11-14 18:35       ` Alejandro Colomar
2026-11-14 15:53   ` Joe Perches
2026-11-14 16:59     ` Alejandro Colomar
2026-11-14 18:24       ` Joe Perches
2026-11-14 19:00         ` Alejandro Colomar
2026-11-14 19:49     ` Off-by-one in arch/mips/bcm47xx/time.c (was: [PATCH v6 02/19] string: Add strprefix()) Alejandro Colomar
2026-11-14 15:22 ` [PATCH v6 03/19] string: Document corner-case misbehavior in str_has_prefix() for empty prefix Alejandro Colomar
2026-11-14 15:22 ` [PATCH v6 04/19] device property: Fix fwnode_name_eq() for empty node names Alejandro Colomar
2026-11-14 15:22 ` [PATCH v6 05/19] stddef: Add const_cast() Alejandro Colomar
2026-11-14 15:22 ` [PATCH v6 06/19] string: Add [C]QChar_of() and QVoid_of() Alejandro Colomar
2026-11-14 15:22 ` [PATCH v6 07/19] string: Make strprefix() const-qualifier-generic Alejandro Colomar
2026-11-14 15:22 ` [PATCH v6 08/19] perf map: Use bool for return type instead of int Alejandro Colomar
2026-11-14 15:22 ` [PATCH v6 09/19] string: Make strstarts() a legacy alias for strprefix() Alejandro Colomar
2026-11-14 15:33   ` sashiko-bot
2026-11-14 15:51     ` Alejandro Colomar
2026-11-14 17:52       ` Alejandro Colomar
2026-11-14 15:23 ` [PATCH v6 10/19] platform/x86: hp-bioscfg: Rename local variable Alejandro Colomar
2026-11-14 16:04   ` Joe Perches
2026-11-14 17:08     ` Alejandro Colomar
2026-11-14 15:23 ` [PATCH v6 11/19] treewide: s/strstarts/strprefix/g Alejandro Colomar
2026-11-14 15:23 ` [PATCH v6 12/19] string: Remove strstarts() Alejandro Colomar
2026-11-14 15:29   ` sashiko-bot
2026-11-14 15:45     ` Alejandro Colomar
2026-11-14 15:23 ` [PATCH v6 13/19] s390/test_unwind: Use bool for booleans Alejandro Colomar
2026-11-14 15:23 ` [PATCH v6 14/19] treewide: Replace boolean uses of str_has_prefix() by strprefix() Alejandro Colomar
2026-11-14 15:23 ` [PATCH v6 15/19] treewide: Replace some " Alejandro Colomar
2026-11-14 15:34   ` sashiko-bot
2026-11-14 15:47     ` Alejandro Colomar
2026-11-14 15:23 ` [PATCH v6 16/19] string: Add strsuffix() Alejandro Colomar
2026-11-14 15:24 ` [PATCH v6 17/19] string: Make strends() a legacy alias for strsuffix() Alejandro Colomar
2026-11-14 15:24 ` [PATCH v6 18/19] treewide: s/\<strends\>/strsuffix/g Alejandro Colomar
2026-11-14 15:24 ` [PATCH v6 19/19] string: Remove strends() Alejandro Colomar

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox