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 /fs/proc/task_nommu.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 'fs/proc/task_nommu.c')
| -rw-r--r-- | fs/proc/task_nommu.c | 296 |
1 files changed, 296 insertions, 0 deletions
diff --git a/fs/proc/task_nommu.c b/fs/proc/task_nommu.c new file mode 100644 index 000000000..d362919f4 --- /dev/null +++ b/fs/proc/task_nommu.c @@ -0,0 +1,296 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/mm.h> +#include <linux/file.h> +#include <linux/fdtable.h> +#include <linux/fs_struct.h> +#include <linux/mount.h> +#include <linux/ptrace.h> +#include <linux/slab.h> +#include <linux/seq_file.h> +#include <linux/sched/mm.h> + +#include "internal.h" + +/* + * Logic: we've got two memory sums for each process, "shared", and + * "non-shared". Shared memory may get counted more than once, for + * each process that owns it. Non-shared memory is counted + * accurately. + */ +void task_mem(struct seq_file *m, struct mm_struct *mm) +{ + VMA_ITERATOR(vmi, mm, 0); + struct vm_area_struct *vma; + struct vm_region *region; + unsigned long bytes = 0, sbytes = 0, slack = 0, size; + + mmap_read_lock(mm); + for_each_vma(vmi, vma) { + bytes += kobjsize(vma); + + region = vma->vm_region; + if (region) { + size = kobjsize(region); + size += region->vm_end - region->vm_start; + } else { + size = vma->vm_end - vma->vm_start; + } + + if (atomic_read(&mm->mm_count) > 1 || + is_nommu_shared_mapping(vma->vm_flags)) { + sbytes += size; + } else { + bytes += size; + if (region) + slack = region->vm_end - vma->vm_end; + } + } + + if (atomic_read(&mm->mm_count) > 1) + sbytes += kobjsize(mm); + else + bytes += kobjsize(mm); + + if (current->fs && current->fs->users > 1) + sbytes += kobjsize(current->fs); + else + bytes += kobjsize(current->fs); + + if (current->files && atomic_read(¤t->files->count) > 1) + sbytes += kobjsize(current->files); + else + bytes += kobjsize(current->files); + + if (current->sighand && refcount_read(¤t->sighand->count) > 1) + sbytes += kobjsize(current->sighand); + else + bytes += kobjsize(current->sighand); + + bytes += kobjsize(current); /* includes kernel stack */ + + mmap_read_unlock(mm); + + seq_printf(m, + "Mem:\t%8lu bytes\n" + "Slack:\t%8lu bytes\n" + "Shared:\t%8lu bytes\n", + bytes, slack, sbytes); +} + +unsigned long task_vsize(struct mm_struct *mm) +{ + VMA_ITERATOR(vmi, mm, 0); + struct vm_area_struct *vma; + unsigned long vsize = 0; + + mmap_read_lock(mm); + for_each_vma(vmi, vma) + vsize += vma->vm_end - vma->vm_start; + mmap_read_unlock(mm); + return vsize; +} + +unsigned long task_statm(struct mm_struct *mm, + unsigned long *shared, unsigned long *text, + unsigned long *data, unsigned long *resident) +{ + VMA_ITERATOR(vmi, mm, 0); + struct vm_area_struct *vma; + struct vm_region *region; + unsigned long size = kobjsize(mm); + + mmap_read_lock(mm); + for_each_vma(vmi, vma) { + size += kobjsize(vma); + region = vma->vm_region; + if (region) { + size += kobjsize(region); + size += region->vm_end - region->vm_start; + } + } + + *text = (PAGE_ALIGN(mm->end_code) - (mm->start_code & PAGE_MASK)) + >> PAGE_SHIFT; + *data = (PAGE_ALIGN(mm->start_stack) - (mm->start_data & PAGE_MASK)) + >> PAGE_SHIFT; + mmap_read_unlock(mm); + size >>= PAGE_SHIFT; + size += *text + *data; + *resident = size; + return size; +} + +/* + * display a single VMA to a sequenced file + */ +static int nommu_vma_show(struct seq_file *m, struct vm_area_struct *vma) +{ + struct mm_struct *mm = vma->vm_mm; + unsigned long ino = 0; + struct file *file; + dev_t dev = 0; + int flags; + unsigned long long pgoff = 0; + + flags = vma->vm_flags; + file = vma->vm_file; + + if (file) { + struct inode *inode = file_inode(vma->vm_file); + dev = inode->i_sb->s_dev; + ino = inode->i_ino; + pgoff = (loff_t)vma->vm_pgoff << PAGE_SHIFT; + } + + seq_setwidth(m, 25 + sizeof(void *) * 6 - 1); + seq_printf(m, + "%08lx-%08lx %c%c%c%c %08llx %02x:%02x %lu ", + vma->vm_start, + vma->vm_end, + flags & VM_READ ? 'r' : '-', + flags & VM_WRITE ? 'w' : '-', + flags & VM_EXEC ? 'x' : '-', + flags & VM_MAYSHARE ? flags & VM_SHARED ? 'S' : 's' : 'p', + pgoff, + MAJOR(dev), MINOR(dev), ino); + + if (file) { + seq_pad(m, ' '); + seq_path(m, file_user_path(file), ""); + } else if (mm && vma_is_initial_stack(vma)) { + seq_pad(m, ' '); + seq_puts(m, "[stack]"); + } + + seq_putc(m, '\n'); + return 0; +} + +/* + * display mapping lines for a particular process's /proc/pid/maps + */ +static int show_map(struct seq_file *m, void *_p) +{ + return nommu_vma_show(m, _p); +} + +static struct vm_area_struct *proc_get_vma(struct proc_maps_private *priv, + loff_t *ppos) +{ + struct vm_area_struct *vma = vma_next(&priv->iter); + + if (vma) { + *ppos = vma->vm_start; + } else { + *ppos = -1UL; + } + + return vma; +} + +static void *m_start(struct seq_file *m, loff_t *ppos) +{ + struct proc_maps_private *priv = m->private; + unsigned long last_addr = *ppos; + struct mm_struct *mm; + + /* See proc_get_vma(). Zero at the start or after lseek. */ + if (last_addr == -1UL) + return NULL; + + /* pin the task and mm whilst we play with them */ + priv->task = get_proc_task(priv->inode); + if (!priv->task) + return ERR_PTR(-ESRCH); + + mm = priv->lock_ctx.mm; + if (!mm || !mmget_not_zero(mm)) { + put_task_struct(priv->task); + priv->task = NULL; + return NULL; + } + + if (mmap_read_lock_killable(mm)) { + mmput(mm); + put_task_struct(priv->task); + priv->task = NULL; + return ERR_PTR(-EINTR); + } + + vma_iter_init(&priv->iter, mm, last_addr); + + return proc_get_vma(priv, ppos); +} + +static void m_stop(struct seq_file *m, void *v) +{ + struct proc_maps_private *priv = m->private; + struct mm_struct *mm = priv->lock_ctx.mm; + + if (!priv->task) + return; + + mmap_read_unlock(mm); + mmput(mm); + put_task_struct(priv->task); + priv->task = NULL; +} + +static void *m_next(struct seq_file *m, void *_p, loff_t *ppos) +{ + return proc_get_vma(m->private, ppos); +} + +static const struct seq_operations proc_pid_maps_ops = { + .start = m_start, + .next = m_next, + .stop = m_stop, + .show = show_map +}; + +static int maps_open(struct inode *inode, struct file *file, + const struct seq_operations *ops) +{ + struct proc_maps_private *priv; + + priv = __seq_open_private(file, ops, sizeof(*priv)); + if (!priv) + return -ENOMEM; + + priv->inode = inode; + priv->lock_ctx.mm = proc_mem_open(inode, PTRACE_MODE_READ); + if (IS_ERR_OR_NULL(priv->lock_ctx.mm)) { + int err = priv->lock_ctx.mm ? PTR_ERR(priv->lock_ctx.mm) : -ESRCH; + + seq_release_private(inode, file); + return err; + } + + return 0; +} + + +static int map_release(struct inode *inode, struct file *file) +{ + struct seq_file *seq = file->private_data; + struct proc_maps_private *priv = seq->private; + + if (priv->lock_ctx.mm) + mmdrop(priv->lock_ctx.mm); + + return seq_release_private(inode, file); +} + +static int pid_maps_open(struct inode *inode, struct file *file) +{ + return maps_open(inode, file, &proc_pid_maps_ops); +} + +const struct file_operations proc_pid_maps_operations = { + .open = pid_maps_open, + .read = seq_read, + .llseek = seq_lseek, + .release = map_release, +}; + |
