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