mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-29 03:01:39 -04:00
vmalloc: fix remap_vmalloc_range() bounds checks
commit bdebd6a2831b6fab69eb85cee74a8ba77f1a1cc2 upstream.
remap_vmalloc_range() has had various issues with the bounds checks it
promises to perform ("This function checks that addr is a valid
vmalloc'ed area, and that it is big enough to cover the vma") over time,
e.g.:
- not detecting pgoff<<PAGE_SHIFT overflow
- not detecting (pgoff<<PAGE_SHIFT)+usize overflow
- not checking whether addr and addr+(pgoff<<PAGE_SHIFT) are the same
vmalloc allocation
- comparing a potentially wildly out-of-bounds pointer with the end of
the vmalloc region
In particular, since commit fc9702273e2e ("bpf: Add mmap() support for
BPF_MAP_TYPE_ARRAY"), unprivileged users can cause kernel null pointer
dereferences by calling mmap() on a BPF map with a size that is bigger
than the distance from the start of the BPF map to the end of the
address space.
This could theoretically be used as a kernel ASLR bypass, by using
whether mmap() with a given offset oopses or returns an error code to
perform a binary search over the possible address range.
To allow remap_vmalloc_range_partial() to verify that addr and
addr+(pgoff<<PAGE_SHIFT) are in the same vmalloc region, pass the offset
to remap_vmalloc_range_partial() instead of adding it to the pointer in
remap_vmalloc_range().
In remap_vmalloc_range_partial(), fix the check against
get_vm_area_size() by using size comparisons instead of pointer
comparisons, and add checks for pgoff.
Fixes: 833423143c ("[PATCH] mm: introduce remap_vmalloc_range()")
Signed-off-by: Jann Horn <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Cc: [email protected]
Cc: Alexei Starovoitov <[email protected]>
Cc: Daniel Borkmann <[email protected]>
Cc: Martin KaFai Lau <[email protected]>
Cc: Song Liu <[email protected]>
Cc: Yonghong Song <[email protected]>
Cc: Andrii Nakryiko <[email protected]>
Cc: John Fastabend <[email protected]>
Cc: KP Singh <[email protected]>
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Linus Torvalds <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
This commit is contained in:
parent
3d15344e23
commit
f4f235309b
4 changed files with 18 additions and 7 deletions
16
mm/vmalloc.c
16
mm/vmalloc.c
|
|
@ -34,6 +34,7 @@
|
|||
#include <linux/llist.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/rbtree_augmented.h>
|
||||
#include <linux/overflow.h>
|
||||
|
||||
#include <linux/uaccess.h>
|
||||
#include <asm/tlbflush.h>
|
||||
|
|
@ -2976,6 +2977,7 @@ finished:
|
|||
* @vma: vma to cover
|
||||
* @uaddr: target user address to start at
|
||||
* @kaddr: virtual address of vmalloc kernel memory
|
||||
* @pgoff: offset from @kaddr to start at
|
||||
* @size: size of map area
|
||||
*
|
||||
* Returns: 0 for success, -Exxx on failure
|
||||
|
|
@ -2988,9 +2990,15 @@ finished:
|
|||
* Similar to remap_pfn_range() (see mm/memory.c)
|
||||
*/
|
||||
int remap_vmalloc_range_partial(struct vm_area_struct *vma, unsigned long uaddr,
|
||||
void *kaddr, unsigned long size)
|
||||
void *kaddr, unsigned long pgoff,
|
||||
unsigned long size)
|
||||
{
|
||||
struct vm_struct *area;
|
||||
unsigned long off;
|
||||
unsigned long end_index;
|
||||
|
||||
if (check_shl_overflow(pgoff, PAGE_SHIFT, &off))
|
||||
return -EINVAL;
|
||||
|
||||
size = PAGE_ALIGN(size);
|
||||
|
||||
|
|
@ -3004,8 +3012,10 @@ int remap_vmalloc_range_partial(struct vm_area_struct *vma, unsigned long uaddr,
|
|||
if (!(area->flags & (VM_USERMAP | VM_DMA_COHERENT)))
|
||||
return -EINVAL;
|
||||
|
||||
if (kaddr + size > area->addr + get_vm_area_size(area))
|
||||
if (check_add_overflow(size, off, &end_index) ||
|
||||
end_index > get_vm_area_size(area))
|
||||
return -EINVAL;
|
||||
kaddr += off;
|
||||
|
||||
do {
|
||||
struct page *page = vmalloc_to_page(kaddr);
|
||||
|
|
@ -3044,7 +3054,7 @@ int remap_vmalloc_range(struct vm_area_struct *vma, void *addr,
|
|||
unsigned long pgoff)
|
||||
{
|
||||
return remap_vmalloc_range_partial(vma, vma->vm_start,
|
||||
addr + (pgoff << PAGE_SHIFT),
|
||||
addr, pgoff,
|
||||
vma->vm_end - vma->vm_start);
|
||||
}
|
||||
EXPORT_SYMBOL(remap_vmalloc_range);
|
||||
|
|
|
|||
Loading…
Reference in a new issue