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, ©_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]>