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

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

             reply	other threads:[~2026-11-14 15:21 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-14 15:21 Alejandro Colomar [this message]
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 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
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:23 ` [PATCH v6 10/19] platform/x86: hp-bioscfg: Rename local variable 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
2026-11-14 15:23 ` [PATCH v6 12/19] string: Remove strstarts() 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: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

Reply instructions:

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

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

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

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

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

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

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