diff options
| author | Muhammad Bilal <meatuni001@gmail.com> | 2026-07-28 17:54:55 +0500 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2026-09-01 12:15:16 +0200 |
| commit | ff917923f4fb9c83717ba135ee47d7e4c1567bb7 (patch) | |
| tree | c38fd8fcf58e1937b17fdbc6518b6f8b797fc15a /drivers/of/pdt.c | |
| download | linux-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 'drivers/of/pdt.c')
| -rw-r--r-- | drivers/of/pdt.c | 215 |
1 files changed, 215 insertions, 0 deletions
diff --git a/drivers/of/pdt.c b/drivers/of/pdt.c new file mode 100644 index 000000000..cb0cb374b --- /dev/null +++ b/drivers/of/pdt.c @@ -0,0 +1,215 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* pdt.c: OF PROM device tree support code. + * + * Paul Mackerras August 1996. + * Copyright (C) 1996-2005 Paul Mackerras. + * + * Adapted for 64bit PowerPC by Dave Engebretsen and Peter Bergner. + * {engebret|bergner}@us.ibm.com + * + * Adapted for sparc by David S. Miller davem@davemloft.net + * Adapted for multiple architectures by Andres Salomon <dilinger@queued.net> + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/mutex.h> +#include <linux/slab.h> +#include <linux/of.h> +#include <linux/of_pdt.h> + +#include "of_private.h" + +static struct of_pdt_ops *of_pdt_prom_ops __initdata; + +#if defined(CONFIG_SPARC) +unsigned int of_pdt_unique_id __initdata; + +#define of_pdt_incr_unique_id(p) do { \ + (p)->unique_id = of_pdt_unique_id++; \ +} while (0) + +static char * __init of_pdt_build_full_name(struct device_node *dp) +{ + return build_path_component(dp); +} + +#else /* CONFIG_SPARC */ + +static inline void of_pdt_incr_unique_id(void *p) { } +static inline void irq_trans_init(struct device_node *dp) { } + +static char * __init of_pdt_build_full_name(struct device_node *dp) +{ + static int failsafe_id = 0; /* for generating unique names on failure */ + const char *name; + char path[256]; + char *buf; + int len; + + if (!of_pdt_prom_ops->pkg2path(dp->phandle, path, sizeof(path), &len)) { + name = kbasename(path); + buf = prom_early_alloc(strlen(name) + 1); + strcpy(buf, name); + return buf; + } + + name = of_get_property(dp, "name", &len); + buf = prom_early_alloc(len + 16); + sprintf(buf, "%s@unknown%i", name, failsafe_id++); + pr_err("%s: pkg2path failed; assigning %s\n", __func__, buf); + return buf; +} + +#endif /* !CONFIG_SPARC */ + +static struct property * __init of_pdt_build_one_prop(phandle node, char *prev, + char *special_name, + void *special_val, + int special_len) +{ + static struct property *tmp = NULL; + struct property *p; + int err; + + if (tmp) { + p = tmp; + memset(p, 0, sizeof(*p) + 32); + tmp = NULL; + } else { + p = prom_early_alloc(sizeof(struct property) + 32); + of_pdt_incr_unique_id(p); + } + + p->name = (char *) (p + 1); + if (special_name) { + strcpy(p->name, special_name); + p->length = special_len; + p->value = prom_early_alloc(special_len); + memcpy(p->value, special_val, special_len); + } else { + err = of_pdt_prom_ops->nextprop(node, prev, p->name); + if (err) { + tmp = p; + return NULL; + } + p->length = of_pdt_prom_ops->getproplen(node, p->name); + if (p->length <= 0) { + p->length = 0; + } else { + int len; + + p->value = prom_early_alloc(p->length + 1); + len = of_pdt_prom_ops->getproperty(node, p->name, + p->value, p->length); + if (len <= 0) + p->length = 0; + ((unsigned char *)p->value)[p->length] = '\0'; + } + } + return p; +} + +static struct property * __init of_pdt_build_prop_list(phandle node) +{ + struct property *head, *tail; + + head = tail = of_pdt_build_one_prop(node, NULL, + ".node", &node, sizeof(node)); + + tail->next = of_pdt_build_one_prop(node, NULL, NULL, NULL, 0); + tail = tail->next; + while(tail) { + tail->next = of_pdt_build_one_prop(node, tail->name, + NULL, NULL, 0); + tail = tail->next; + } + + return head; +} + +static char * __init of_pdt_get_one_property(phandle node, const char *name) +{ + char *buf = "<NULL>"; + int len; + + len = of_pdt_prom_ops->getproplen(node, name); + if (len > 0) { + buf = prom_early_alloc(len); + len = of_pdt_prom_ops->getproperty(node, name, buf, len); + } + + return buf; +} + +static struct device_node * __init of_pdt_create_node(phandle node, + struct device_node *parent) +{ + struct device_node *dp; + + if (!node) + return NULL; + + dp = prom_early_alloc(sizeof(*dp)); + of_node_init(dp); + of_pdt_incr_unique_id(dp); + dp->parent = parent; + + dp->name = of_pdt_get_one_property(node, "name"); + dp->phandle = node; + + dp->properties = of_pdt_build_prop_list(node); + + dp->full_name = of_pdt_build_full_name(dp); + + irq_trans_init(dp); + + return dp; +} + +static struct device_node * __init of_pdt_build_tree(struct device_node *parent, + phandle node) +{ + struct device_node *ret = NULL, *prev_sibling = NULL; + struct device_node *dp; + + while (1) { + dp = of_pdt_create_node(node, parent); + if (!dp) + break; + + if (prev_sibling) + prev_sibling->sibling = dp; + + if (!ret) + ret = dp; + prev_sibling = dp; + + dp->child = of_pdt_build_tree(dp, of_pdt_prom_ops->getchild(node)); + + node = of_pdt_prom_ops->getsibling(node); + } + + return ret; +} + +static void * __init kernel_tree_alloc(u64 size, u64 align) +{ + return prom_early_alloc(size); +} + +void __init of_pdt_build_devicetree(phandle root_node, struct of_pdt_ops *ops) +{ + BUG_ON(!ops); + of_pdt_prom_ops = ops; + + of_root = of_pdt_create_node(root_node, NULL); + of_root->full_name = "/"; + + of_root->child = of_pdt_build_tree(of_root, + of_pdt_prom_ops->getchild(of_root->phandle)); + + /* Get pointer to "/chosen" and "/aliases" nodes for use everywhere */ + of_alias_scan(kernel_tree_alloc); +} |
