On Wed, Jul 22, 2026 at 10:43 PM Dev Jain <[email protected]> wrote:
>
> The vmalloc allocator stores the actual allocation size inside the
> vm_struct structure. We can use this bound in usercopy instead of the
> page-aligned va_end to catch usercopy beyond the actual allocation size.
>
> For vmap, the requested_size field is always page-aligned since it maps a
> certain number of pages. Same for vm_map_ram (alongwith, not even having
> a vm_struct). So the check is only relevant for vmalloc mappings.
>
> Because there are early vm areas registered even before vmalloc_init,
> requested_size may be zero. So also check whether the requested_size
> is set.
>
> Signed-off-by: Dev Jain <[email protected]>
> ---

I wrote the test module below to verify the impact on user-space behavior.

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/uaccess.h>
#include <linux/vmalloc.h>

#define PROC_NAME "copy_bug"

static ssize_t copy_bug_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
        volatile int alloc_len = 5 * 1024, copy_len = 6 * 1024;
        void *kbuf;
        int ret;

        if (*ppos)
                return 0;

        kbuf = vmalloc(alloc_len);
        if (!kbuf)
                return -ENOMEM;
        memset(kbuf, 0, alloc_len);

        ret = copy_to_user(buf, kbuf, copy_len);
        pr_info("copy_bug: copy_to_user returned %d count:%ld\n", ret, count);
        vfree(kbuf);
        return count;
}

static const struct proc_ops copy_bug_ops = {
        .proc_read = copy_bug_read,
};

static int __init copy_bug_init(void)
{
        proc_create(PROC_NAME, 0444, NULL, &copy_bug_ops);
        return 0;
}

static void __exit copy_bug_exit(void)
{
        remove_proc_entry(PROC_NAME, NULL);
}

module_init(copy_bug_init);
module_exit(copy_bug_exit);
MODULE_LICENSE("GPL");

without the patch:
/ # cat /proc/copy_bug
[    6.517711] copy_bug: copy_to_user returned 0 count:131072
[    6.655611] copy_bug: copy_to_user returned 0 count:131072
[    6.783372] copy_bug: copy_to_user returned 0 count:131072
[    6.912167] copy_bug: copy_to_user returned 0 count:131072
[    7.039019] copy_bug: copy_to_user returned 0 count:131072

with the patch:

/ # cat /proc/copy_bug
[   11.298890] usercopy: Kernel memory exposure attempt detected from
vmalloc (offset 0, size 6144)!
[   11.299246] ------------[ cut here ]------------
[   11.299276] kernel BUG at mm/usercopy.c:102!
[   11.299616] Internal error: Oops - BUG: 00000000f2000800 [#1]  SMP
[   11.300458] CPU: 1 UID: 0 PID: 74 Comm: cat Not tainted
7.2.0-rc4-g6b5aeb5cf21a-dirty #1033 PREEMPT
[   11.300730] Hardware name: linux,dummy-virt (DT)
[   11.300940] pstate: 60000005 (nZCv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--)
[   11.301088] pc : usercopy_abort+0x94/0x98
[   11.301190] lr : usercopy_abort+0x94/0x98
[   11.301280] sp : ffff800083643c40
[   11.301358] x29: ffff800083643c50 x28: ffff000004373700 x27: 0000000000000000
[   11.301994] x26: 0000000000000000 x25: 0000000000000000 x24: 0000000000000001
[   11.302152] x23: 0000000000020000 x22: 0000000000000001 x21: ffff8000833a6800
[   11.302298] x20: 0000000000001800 x19: ffff8000833a5000 x18: 00000000ffffffff
[   11.302440] x17: 2820636f6c6c616d x16: 76206d6f72662064 x15: 6574636574656420
[   11.302580] x14: 74706d6574746120 x13: 0000000000020000 x12: 0000000000008000
[   11.302720] x11: ffff800082271138 x10: ffff800082181534 x9 : ffff80008012842c
[   11.302878] x8 : ffff000004373700 x7 : ffff80008012782c x6 : 0000000000000001
[   11.303024] x5 : 0000000000001297 x4 : 0000000000000000 x3 : 0000000000000000
[   11.303162] x2 : 0000000000000000 x1 : ffff000004373700 x0 : 0000000000000055
[   11.303378] Call trace:
[   11.303572]  usercopy_abort+0x94/0x98 (P)
[   11.303744]  __check_object_size+0x1dc/0x360
[   11.303834]  copy_bug_read+0x9c/0x1a0
[   11.303910]  proc_reg_read+0xa4/0x100
[   11.303986]  vfs_read+0xcc/0x2f8
[   11.304064]  ksys_read+0x74/0x118
[   11.304134]  __arm64_sys_read+0x24/0x38
[   11.304212]  invoke_syscall+0x5c/0x120
[   11.304288]  el0_svc_common.constprop.0+0x48/0xf0
[   11.304378]  do_el0_svc+0x24/0x38
[   11.304448]  el0_svc+0x58/0x3f0
[   11.304518]  el0t_64_sync_handler+0xa0/0xe8
[   11.304598]  el0t_64_sync+0x1ac/0x1b0
[   11.304814] Code: aa0003e3 d000e3e0 91378000 97ffec92 (d4210000)
[   11.305138] ---[ end trace 0000000000000000 ]---
[   11.305390] note: cat[74] exited with irqs disabled
[   11.305542] note: cat[74] exited with preempt_count 1
[   11.307308] ------------[ cut here ]------------

So:

Tested-by: Barry Song <[email protected]>
Reviewed-by: Barry Song <[email protected]>

Reply via email to