summaryrefslogtreecommitdiffstats
path: root/tools/perf/util/help-unknown-cmd.c
diff options
context:
space:
mode:
authorMuhammad Bilal <meatuni001@gmail.com>2026-07-28 17:54:55 +0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2026-09-01 12:15:16 +0200
commitff917923f4fb9c83717ba135ee47d7e4c1567bb7 (patch)
treec38fd8fcf58e1937b17fdbc6518b6f8b797fc15a /tools/perf/util/help-unknown-cmd.c
downloadlinux-stable-ff917923f4fb9c83717ba135ee47d7e4c1567bb7.tar.gz
linux-stable-ff917923f4fb9c83717ba135ee47d7e4c1567bb7.zip
staging: rtl8723bs: fix OOB read in rtw_action_frame_parse()grafted
rtw_action_frame_parse() takes a frame_len parameter but never actually checks it before indexing into the frame body: const u8 *frame_body = frame + sizeof(struct ieee80211_hdr_3addr); ... c = frame_body[0]; ... a = frame_body[1]; frame_body already points 24 bytes (sizeof(struct ieee80211_hdr_3addr)) into frame, so reading frame_body[0] and frame_body[1] requires frame_len >= 26. A management action frame shorter than that (e.g. exactly 24 bytes, the minimum a malicious peer can send) causes a 1-2 byte out-of-bounds read. This is reachable from rtw_cfg80211_monitor_if_xmit_entry() and cfg80211_rtw_mgmt_tx() in ioctl_cfg80211.c, both of which pass attacker/user-influenced frame buffers and lengths straight through. Add the missing length check before frame_body is dereferenced. Fixes: 554c0a3abf216 ("staging: Add rtl8723bs sdio wifi driver") Cc: stable@vger.kernel.org Signed-off-by: Muhammad Bilal <meatuni001@gmail.com> Link: https://patch.msgid.link/20260728125456.32359-3-meatuni001@gmail.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'tools/perf/util/help-unknown-cmd.c')
-rw-r--r--tools/perf/util/help-unknown-cmd.c122
1 files changed, 122 insertions, 0 deletions
diff --git a/tools/perf/util/help-unknown-cmd.c b/tools/perf/util/help-unknown-cmd.c
new file mode 100644
index 000000000..a0a46e34f
--- /dev/null
+++ b/tools/perf/util/help-unknown-cmd.c
@@ -0,0 +1,122 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "cache.h"
+#include "config.h"
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <subcmd/help.h>
+#include "../builtin.h"
+#include "levenshtein.h"
+#include <linux/zalloc.h>
+
+static int autocorrect;
+
+static int perf_unknown_cmd_config(const char *var, const char *value,
+ void *cb __maybe_unused)
+{
+ if (!strcmp(var, "help.autocorrect"))
+ return perf_config_int(&autocorrect, var,value);
+
+ return 0;
+}
+
+static int levenshtein_compare(const void *p1, const void *p2)
+{
+ const struct cmdname *const *c1 = p1, *const *c2 = p2;
+ const char *s1 = (*c1)->name, *s2 = (*c2)->name;
+ int l1 = (*c1)->len;
+ int l2 = (*c2)->len;
+ return l1 != l2 ? l1 - l2 : strcmp(s1, s2);
+}
+
+static int add_cmd_list(struct cmdnames *cmds, struct cmdnames *old)
+{
+ unsigned int i, nr = cmds->cnt + old->cnt;
+ void *tmp;
+
+ if (nr > cmds->alloc) {
+ /* Choose bigger one to alloc */
+ if (alloc_nr(cmds->alloc) < nr)
+ cmds->alloc = nr;
+ else
+ cmds->alloc = alloc_nr(cmds->alloc);
+ tmp = realloc(cmds->names, cmds->alloc * sizeof(*cmds->names));
+ if (!tmp)
+ return -1;
+ cmds->names = tmp;
+ }
+ for (i = 0; i < old->cnt; i++)
+ cmds->names[cmds->cnt++] = old->names[i];
+ zfree(&old->names);
+ old->cnt = 0;
+ return 0;
+}
+
+const char *help_unknown_cmd(const char *cmd, struct cmdnames *main_cmds)
+{
+ unsigned int i, n = 0, best_similarity = 0;
+ struct cmdnames other_cmds;
+
+ memset(&other_cmds, 0, sizeof(other_cmds));
+
+ perf_config(perf_unknown_cmd_config, NULL);
+
+ load_command_list("perf-", main_cmds, &other_cmds);
+
+ if (add_cmd_list(main_cmds, &other_cmds) < 0) {
+ fprintf(stderr, "ERROR: Failed to allocate command list for unknown command.\n");
+ goto end;
+ }
+ qsort(main_cmds->names, main_cmds->cnt,
+ sizeof(main_cmds->names), cmdname_compare);
+ uniq(main_cmds);
+
+ if (main_cmds->cnt) {
+ /* This reuses cmdname->len for similarity index */
+ for (i = 0; i < main_cmds->cnt; ++i) {
+ main_cmds->names[i]->len =
+ levenshtein(cmd, main_cmds->names[i]->name,
+ /*swap_penalty=*/0,
+ /*substition_penality=*/2,
+ /*insertion_penality=*/1,
+ /*deletion_penalty=*/1);
+ }
+ qsort(main_cmds->names, main_cmds->cnt,
+ sizeof(*main_cmds->names), levenshtein_compare);
+
+ best_similarity = main_cmds->names[0]->len;
+ n = 1;
+ while (n < main_cmds->cnt && best_similarity == main_cmds->names[n]->len)
+ ++n;
+ }
+
+ if (autocorrect && n == 1) {
+ const char *assumed = main_cmds->names[0]->name;
+
+ main_cmds->names[0] = NULL;
+ clean_cmdnames(&other_cmds);
+ fprintf(stderr, "WARNING: You called a perf program named '%s', "
+ "which does not exist.\n"
+ "Continuing under the assumption that you meant '%s'\n",
+ cmd, assumed);
+ if (autocorrect > 0) {
+ fprintf(stderr, "in %0.1f seconds automatically...\n",
+ (float)autocorrect/10.0);
+ poll(NULL, 0, autocorrect * 100);
+ }
+ return assumed;
+ }
+
+ fprintf(stderr, "perf: '%s' is not a perf-command. See 'perf --help'.\n", cmd);
+
+ if (main_cmds->cnt && best_similarity < 6) {
+ fprintf(stderr, "\nDid you mean %s?\n",
+ n < 2 ? "this": "one of these");
+
+ for (i = 0; i < n; i++)
+ fprintf(stderr, "\t%s\n", main_cmds->names[i]->name);
+ }
+end:
+ clean_cmdnames(&other_cmds);
+ return NULL;
+}