summaryrefslogtreecommitdiffstats
path: root/fs/ocfs2/sysfile.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 /fs/ocfs2/sysfile.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 'fs/ocfs2/sysfile.c')
-rw-r--r--fs/ocfs2/sysfile.c164
1 files changed, 164 insertions, 0 deletions
diff --git a/fs/ocfs2/sysfile.c b/fs/ocfs2/sysfile.c
new file mode 100644
index 000000000..67e492f4b
--- /dev/null
+++ b/fs/ocfs2/sysfile.c
@@ -0,0 +1,164 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * sysfile.c
+ *
+ * Initialize, read, write, etc. system files.
+ *
+ * Copyright (C) 2002, 2004 Oracle. All rights reserved.
+ */
+
+#include <linux/fs.h>
+#include <linux/types.h>
+#include <linux/highmem.h>
+
+#include <cluster/masklog.h>
+
+#include "ocfs2.h"
+
+#include "alloc.h"
+#include "dir.h"
+#include "inode.h"
+#include "journal.h"
+#include "sysfile.h"
+
+#include "buffer_head_io.h"
+
+static struct inode * _ocfs2_get_system_file_inode(struct ocfs2_super *osb,
+ int type,
+ u32 slot);
+
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+static struct lock_class_key ocfs2_sysfile_cluster_lock_key[NUM_SYSTEM_INODES];
+#endif
+
+static inline int is_global_system_inode(int type)
+{
+ return type >= OCFS2_FIRST_ONLINE_SYSTEM_INODE &&
+ type <= OCFS2_LAST_GLOBAL_SYSTEM_INODE;
+}
+
+static struct inode **get_local_system_inode(struct ocfs2_super *osb,
+ int type,
+ u32 slot)
+{
+ int index;
+ struct inode **local_system_inodes, **free = NULL;
+
+ BUG_ON(slot == OCFS2_INVALID_SLOT);
+ BUG_ON(type < OCFS2_FIRST_LOCAL_SYSTEM_INODE ||
+ type > OCFS2_LAST_LOCAL_SYSTEM_INODE);
+
+ spin_lock(&osb->osb_lock);
+ local_system_inodes = osb->local_system_inodes;
+ spin_unlock(&osb->osb_lock);
+
+ if (unlikely(!local_system_inodes)) {
+ local_system_inodes =
+ kzalloc(array3_size(sizeof(struct inode *),
+ NUM_LOCAL_SYSTEM_INODES,
+ osb->max_slots),
+ GFP_NOFS);
+ if (!local_system_inodes) {
+ mlog_errno(-ENOMEM);
+ /*
+ * return NULL here so that ocfs2_get_sytem_file_inodes
+ * will try to create an inode and use it. We will try
+ * to initialize local_system_inodes next time.
+ */
+ return NULL;
+ }
+
+ spin_lock(&osb->osb_lock);
+ if (osb->local_system_inodes) {
+ /* Someone has initialized it for us. */
+ free = local_system_inodes;
+ local_system_inodes = osb->local_system_inodes;
+ } else
+ osb->local_system_inodes = local_system_inodes;
+ spin_unlock(&osb->osb_lock);
+ kfree(free);
+ }
+
+ index = (slot * NUM_LOCAL_SYSTEM_INODES) +
+ (type - OCFS2_FIRST_LOCAL_SYSTEM_INODE);
+
+ return &local_system_inodes[index];
+}
+
+struct inode *ocfs2_get_system_file_inode(struct ocfs2_super *osb,
+ int type,
+ u32 slot)
+{
+ struct inode *inode = NULL;
+ struct inode **arr = NULL;
+
+ /* avoid the lookup if cached in local system file array */
+ if (is_global_system_inode(type)) {
+ arr = &(osb->global_system_inodes[type]);
+ } else
+ arr = get_local_system_inode(osb, type, slot);
+
+ if (arr && ((inode = *arr) != NULL)) {
+ /* get a ref in addition to the array ref */
+ inode = igrab(inode);
+ BUG_ON(!inode);
+
+ return inode;
+ }
+
+ /* this gets one ref thru iget */
+ inode = _ocfs2_get_system_file_inode(osb, type, slot);
+
+ /* add one more if putting into array for first time */
+ if (inode && arr && !*arr && !cmpxchg(&(*arr), NULL, inode)) {
+ inode = igrab(inode);
+ BUG_ON(!inode);
+ }
+ return inode;
+}
+
+static struct inode * _ocfs2_get_system_file_inode(struct ocfs2_super *osb,
+ int type,
+ u32 slot)
+{
+ char namebuf[40];
+ struct inode *inode = NULL;
+ u64 blkno;
+ int len, status = 0;
+
+ len = ocfs2_sprintf_system_inode_name(namebuf,
+ sizeof(namebuf),
+ type, slot);
+
+ status = ocfs2_lookup_ino_from_name(osb->sys_root_inode,
+ namebuf, len, &blkno);
+ if (status < 0) {
+ goto bail;
+ }
+
+ inode = ocfs2_iget(osb, blkno, OCFS2_FI_FLAG_SYSFILE, type);
+ if (IS_ERR(inode)) {
+ mlog_errno(PTR_ERR(inode));
+ inode = NULL;
+ goto bail;
+ }
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+ if (type == LOCAL_USER_QUOTA_SYSTEM_INODE ||
+ type == LOCAL_GROUP_QUOTA_SYSTEM_INODE ||
+ type == JOURNAL_SYSTEM_INODE) {
+ /* Ignore inode lock on these inodes as the lock does not
+ * really belong to any process and lockdep cannot handle
+ * that */
+ OCFS2_I(inode)->ip_inode_lockres.l_lockdep_map.key = NULL;
+ } else {
+ lockdep_init_map(&OCFS2_I(inode)->ip_inode_lockres.
+ l_lockdep_map,
+ ocfs2_system_inodes[type].si_name,
+ &ocfs2_sysfile_cluster_lock_key[type], 0);
+ }
+#endif
+bail:
+
+ return inode;
+}
+