Re: [PATCH] mm/usercopy: harden bounds checking for vmalloc allocations
Barry Song <[email protected]> Sat, 25 Jul 2026 13:44:52 +0800
| Newsgroups | org.kernel.vger.linux-hardening,org.kernel.vger.linux-kernel,org.kvack.linux-mm |
|---|---|
| Message-ID | <CAGsJ_4wetarsg0fFvNdeWrCa+Ka97jjmgdRsmPJJZbUFyUxjEw@mail.gmail.com> |
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]>