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 /security/landlock/access.h | |
| 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 'security/landlock/access.h')
| -rw-r--r-- | security/landlock/access.h | 151 |
1 files changed, 151 insertions, 0 deletions
diff --git a/security/landlock/access.h b/security/landlock/access.h new file mode 100644 index 000000000..bbbb41f41 --- /dev/null +++ b/security/landlock/access.h @@ -0,0 +1,151 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Landlock - Access types and helpers + * + * Copyright © 2016-2020 Mickaël Salaün <mic@digikod.net> + * Copyright © 2018-2020 ANSSI + * Copyright © 2024-2025 Microsoft Corporation + */ + +#ifndef _SECURITY_LANDLOCK_ACCESS_H +#define _SECURITY_LANDLOCK_ACCESS_H + +#include <linux/bitops.h> +#include <linux/build_bug.h> +#include <linux/kernel.h> +#include <uapi/linux/landlock.h> + +#include "limits.h" + +/* + * All access rights that are denied by default whether they are handled or not + * by a ruleset/layer. This must be ORed with all domain->handled_masks[] + * entries when we need to get the absolute handled access masks, see + * landlock_upgrade_handled_access_masks(). + */ +/* clang-format off */ +#define _LANDLOCK_ACCESS_FS_INITIALLY_DENIED ( \ + LANDLOCK_ACCESS_FS_REFER) +/* clang-format on */ + +/* clang-format off */ +#define _LANDLOCK_ACCESS_FS_OPTIONAL ( \ + LANDLOCK_ACCESS_FS_TRUNCATE | \ + LANDLOCK_ACCESS_FS_IOCTL_DEV) +/* clang-format on */ + +typedef u32 access_mask_t; + +/* Makes sure all filesystem access rights can be stored. */ +static_assert(BITS_PER_TYPE(access_mask_t) >= LANDLOCK_NUM_ACCESS_FS); +/* Makes sure all network access rights can be stored. */ +static_assert(BITS_PER_TYPE(access_mask_t) >= LANDLOCK_NUM_ACCESS_NET); +/* Makes sure all scoped rights can be stored. */ +static_assert(BITS_PER_TYPE(access_mask_t) >= LANDLOCK_NUM_SCOPE); +/* Makes sure for_each_set_bit() and for_each_clear_bit() calls are OK. */ +static_assert(sizeof(unsigned long) >= sizeof(access_mask_t)); + +/* Ruleset access masks. */ +struct access_masks { + access_mask_t fs : LANDLOCK_NUM_ACCESS_FS; + access_mask_t net : LANDLOCK_NUM_ACCESS_NET; + access_mask_t scope : LANDLOCK_NUM_SCOPE; +} __packed __aligned(sizeof(u32)); + +union access_masks_all { + struct access_masks masks; + u32 all; +}; + +/* Makes sure all fields are covered. */ +static_assert(sizeof(typeof_member(union access_masks_all, masks)) == + sizeof(typeof_member(union access_masks_all, all))); + +/** + * struct layer_mask - The access rights and rule flags for a layer. + * + * This has a bit for each access rights and rule flags. During access checks, + * it is used to represent the access rights for each layer which still need to + * be fulfilled. When all bits are 0, the access request is considered to be + * fulfilled. + */ +struct layer_mask { + /** + * @access: The unfulfilled access rights for this layer. + */ + access_mask_t access : LANDLOCK_NUM_ACCESS_MAX; +#ifdef CONFIG_SECURITY_LANDLOCK_LOG + /** + * @quiet: Whether we have encountered a rule with the quiet flag for + * this layer. Used to control logging. + */ + access_mask_t quiet : 1; +#endif /* CONFIG_SECURITY_LANDLOCK_LOG */ +} __packed __aligned(sizeof(access_mask_t)); + +/* + * Make sure that we don't increase the size of struct layer_mask when storing + * rule flags. + */ +static_assert(sizeof(struct layer_mask) == sizeof(access_mask_t)); + +/** + * struct layer_masks - An array of struct layer_mask, one per layer. + */ +struct layer_masks { + /** + * @layers: The unfulfilled access rights for each layer. + */ + struct layer_mask layers[LANDLOCK_MAX_NUM_LAYERS]; +}; + +/* + * Tracks domains responsible of a denied access. This avoids storing in each + * object the full matrix of per-layer unfulfilled access rights, which is + * required by update_request(). + * + * Each nibble represents the layer index of the newest layer which denied a + * certain access right. For file system access rights, the upper four bits are + * the index of the layer which denies LANDLOCK_ACCESS_FS_IOCTL_DEV and the + * lower nibble represents LANDLOCK_ACCESS_FS_TRUNCATE. + */ +typedef u8 deny_masks_t; + +/* + * Makes sure all optional access rights can be tied to a layer index (cf. + * get_deny_mask). + */ +static_assert(BITS_PER_TYPE(deny_masks_t) >= + (HWEIGHT(LANDLOCK_MAX_NUM_LAYERS - 1) * + HWEIGHT(_LANDLOCK_ACCESS_FS_OPTIONAL))); + +/* LANDLOCK_MAX_NUM_LAYERS must be a power of two (cf. deny_masks_t assert). */ +static_assert(HWEIGHT(LANDLOCK_MAX_NUM_LAYERS) == 1); + +/* Upgrades with all initially denied by default access rights. */ +static inline struct access_masks +landlock_upgrade_handled_access_masks(struct access_masks access_masks) +{ + /* + * All access rights that are denied by default whether they are + * explicitly handled or not. + */ + if (access_masks.fs) + access_masks.fs |= _LANDLOCK_ACCESS_FS_INITIALLY_DENIED; + + return access_masks; +} + +/* Checks the subset relation between access masks. */ +static inline bool access_mask_subset(access_mask_t subset, + access_mask_t superset) +{ + return (subset | superset) == superset; +} + +/* A bitmask that is large enough to hold set of optional accesses. */ +typedef u8 optional_access_t; +static_assert(BITS_PER_TYPE(optional_access_t) >= + HWEIGHT(_LANDLOCK_ACCESS_FS_OPTIONAL)); + +#endif /* _SECURITY_LANDLOCK_ACCESS_H */ |
