summaryrefslogtreecommitdiffstats
path: root/lib/argv_split.c
diff options
context:
space:
mode:
authorNuoqi Gui <gnq25@mails.tsinghua.edu.cn>2026-09-04 12:41:55 +0200
committerAlexei Starovoitov <ast@kernel.org>2026-09-04 12:24:25 -0700
commit2b97956af60810cd382b86b9ce9aea421b889861 (patch)
tree7d9b0d8a91f77e63deff23976f68555a4895346a /lib/argv_split.c
downloadlinux-stable-2b97956af60810cd382b86b9ce9aea421b889861.tar.gz
linux-stable-2b97956af60810cd382b86b9ce9aea421b889861.zip
selftests/bpf: Test rhtab kptr cancellation semanticsgrafted
Resizable hash-map updates and deletions must not perform full special-field destruction in their caller context. In particular, a referenced kptr must remain attached to the allocation until the memory allocator destructor can release it safely. Add separate coverage for both affected paths. The update test stores a task kptr, replaces the ordinary value bytes with BPF_EXIST, and verifies that the kptr survived. The delete test removes an element and exchanges its kptr through the still-valid map-value pointer before the allocation is reclaimed. Both cases observe a NULL kptr when rhtab uses bpf_obj_free_fields(). They recover and release the reference after rhtab switches to cancellation semantics. Signed-off-by: Nuoqi Gui <gnq25@mails.tsinghua.edu.cn> [ kkd: Split update and delete coverage and rewrote the commit log ] Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com> Link: https://lore.kernel.org/r/20260904104203.345917-5-memxor@gmail.com Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Diffstat (limited to 'lib/argv_split.c')
-rw-r--r--lib/argv_split.c95
1 files changed, 95 insertions, 0 deletions
diff --git a/lib/argv_split.c b/lib/argv_split.c
new file mode 100644
index 000000000..e28db8e3b
--- /dev/null
+++ b/lib/argv_split.c
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Helper function for splitting a string into an argv-like array.
+ */
+
+#include <linux/kernel.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+
+static int count_argc(const char *str)
+{
+ int count = 0;
+ bool was_space;
+
+ for (was_space = true; *str; str++) {
+ if (isspace(*str)) {
+ was_space = true;
+ } else if (was_space) {
+ was_space = false;
+ count++;
+ }
+ }
+
+ return count;
+}
+
+/**
+ * argv_free - free an argv
+ * @argv: the argument vector to be freed
+ *
+ * Frees an argv and the strings it points to.
+ */
+void argv_free(char **argv)
+{
+ argv--;
+ kfree(argv[0]);
+ kfree(argv);
+}
+EXPORT_SYMBOL(argv_free);
+
+/**
+ * argv_split - split a string at whitespace, returning an argv
+ * @gfp: the GFP mask used to allocate memory
+ * @str: the string to be split
+ * @argcp: returned argument count
+ *
+ * Returns: an array of pointers to strings which are split out from
+ * @str. This is performed by strictly splitting on white-space; no
+ * quote processing is performed. Multiple whitespace characters are
+ * considered to be a single argument separator. The returned array
+ * is always NULL-terminated. Returns NULL on memory allocation
+ * failure.
+ *
+ * The source string at `str' may be undergoing concurrent alteration via
+ * userspace sysctl activity (at least). The argv_split() implementation
+ * attempts to handle this gracefully by taking a local copy to work on.
+ */
+char **argv_split(gfp_t gfp, const char *str, int *argcp)
+{
+ char *argv_str;
+ bool was_space;
+ char **argv, **argv_ret;
+ int argc;
+
+ argv_str = kstrndup(str, KMALLOC_MAX_SIZE - 1, gfp);
+ if (!argv_str)
+ return NULL;
+
+ argc = count_argc(argv_str);
+ argv = kmalloc_array(argc + 2, sizeof(*argv), gfp);
+ if (!argv) {
+ kfree(argv_str);
+ return NULL;
+ }
+
+ *argv = argv_str;
+ argv_ret = ++argv;
+ for (was_space = true; *argv_str; argv_str++) {
+ if (isspace(*argv_str)) {
+ was_space = true;
+ *argv_str = 0;
+ } else if (was_space) {
+ was_space = false;
+ *argv++ = argv_str;
+ }
+ }
+ *argv = NULL;
+
+ if (argcp)
+ *argcp = argc;
+ return argv_ret;
+}
+EXPORT_SYMBOL(argv_split);