summaryrefslogtreecommitdiffstats
path: root/lib/argv_split.c
diff options
context:
space:
mode:
authorFan Wu <fanwu01@zju.edu.cn>2026-08-20 13:53:06 +0000
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2026-09-01 16:50:04 +0200
commit263f7d61a4201cde16849b2d016251806e7418be (patch)
tree861df7c5b878b85741cf319ece81111b4c41c3b1 /lib/argv_split.c
downloadlinux-stable-263f7d61a4201cde16849b2d016251806e7418be.tar.gz
linux-stable-263f7d61a4201cde16849b2d016251806e7418be.zip
usb: typec: qcom-pmic-typec: disable cc_debounce_dwork on stopgrafted
cc_debounce_dwork is queued from the set_cc() and start_toggling() callbacks, which run from TCPM's kthread worker. port_stop() returns before tcpm_unregister_port() destroys that worker. Flushing the worker during unregister may therefore run a callback which queues the delayed work after port_stop() has returned. The delayed work can then run after devres has freed pmic_typec_port. Use disable_delayed_work_sync() in port_stop() to cancel a pending instance and prevent the TCPM callbacks from queueing another one. This issue was found by an in-house static analysis tool. Fixes: a4422ff22142 ("usb: typec: qcom: Add Qualcomm PMIC Type-C driver") Cc: stable <stable@kernel.org> # v6.10+ Assisted-by: Codex:gpt-5.6 Signed-off-by: Fan Wu <fanwu01@zju.edu.cn> Acked-by: Heikki Krogerus <heikki.krogerus@linux.intel.com> Link: https://patch.msgid.link/20260820135307.153773-2-fanwu01@zju.edu.cn Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.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);