mm/usercopy: Detect vmalloc overruns
authorMatthew Wilcox (Oracle) <willy@infradead.org>
Mon, 10 Jan 2022 23:15:28 +0000 (23:15 +0000)
committerKees Cook <keescook@chromium.org>
Wed, 13 Apr 2022 19:15:51 +0000 (12:15 -0700)
If you have a vmalloc() allocation, or an address from calling vmap(),
you cannot overrun the vm_area which describes it, regardless of the
size of the underlying allocation.  This probably doesn't do much for
security because vmalloc comes with guard pages these days, but it
prevents usercopy aborts when copying to a vmap() of smaller pages.

Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Acked-by: Kees Cook <keescook@chromium.org>
Signed-off-by: Kees Cook <keescook@chromium.org>
Link: https://lore.kernel.org/r/20220110231530.665970-3-willy@infradead.org
mm/usercopy.c

index ff13e7708faa226a0efe301537ee4525be5dcddb..e1e856dca124614c545f0ac07ba6a4f1af4cfeb0 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/sched/task.h>
 #include <linux/sched/task_stack.h>
 #include <linux/thread_info.h>
+#include <linux/vmalloc.h>
 #include <linux/atomic.h>
 #include <linux/jump_label.h>
 #include <asm/sections.h>
@@ -238,6 +239,21 @@ static inline void check_heap_object(const void *ptr, unsigned long n,
                return;
        }
 
+       if (is_vmalloc_addr(ptr)) {
+               struct vm_struct *area = find_vm_area(ptr);
+               unsigned long offset;
+
+               if (!area) {
+                       usercopy_abort("vmalloc", "no area", to_user, 0, n);
+                       return;
+               }
+
+               offset = ptr - area->addr;
+               if (offset + n > get_vm_area_size(area))
+                       usercopy_abort("vmalloc", NULL, to_user, offset, n);
+               return;
+       }
+
        folio = virt_to_folio(ptr);
 
        if (folio_test_slab(folio)) {