Commit 1dfbe9fc authored by Matthew Wilcox (Oracle)'s avatar Matthew Wilcox (Oracle) Committed by Kees Cook
Browse files

usercopy: Make usercopy resilient against ridiculously large copies



If 'n' is so large that it's negative, we might wrap around and mistakenly
think that the copy is OK when it's not.  Such a copy would probably
crash, but just doing the arithmetic in a more simple way lets us detect
and refuse this case.

Signed-off-by: default avatarMatthew Wilcox (Oracle) <willy@infradead.org>
Reviewed-by: default avatarUladzislau Rezki (Sony) <urezki@gmail.com>
Tested-by: default avatarZorro Lang <zlang@redhat.com>
Signed-off-by: default avatarKees Cook <keescook@chromium.org>
Link: https://lore.kernel.org/r/20220612213227.3881769-4-willy@infradead.org
parent 35fb9ae4
Loading
Loading
Loading
Loading
+9 −10
Original line number Diff line number Diff line
@@ -162,27 +162,26 @@ static inline void check_heap_object(const void *ptr, unsigned long n,
				     bool to_user)
{
	uintptr_t addr = (uintptr_t)ptr;
	unsigned long offset;
	struct folio *folio;

	if (is_kmap_addr(ptr)) {
		unsigned long page_end = addr | (PAGE_SIZE - 1);

		if (addr + n - 1 > page_end)
			usercopy_abort("kmap", NULL, to_user,
					offset_in_page(ptr), n);
		offset = offset_in_page(ptr);
		if (n > PAGE_SIZE - offset)
			usercopy_abort("kmap", NULL, to_user, offset, n);
		return;
	}

	if (is_vmalloc_addr(ptr)) {
		struct vmap_area *area = find_vmap_area(addr);
		unsigned long offset;

		if (!area)
			usercopy_abort("vmalloc", "no area", to_user, 0, n);

		if (n > area->va_end - addr) {
			offset = addr - area->va_start;
		if (addr + n > area->va_end)
			usercopy_abort("vmalloc", NULL, to_user, offset, n);
		}
		return;
	}

@@ -195,8 +194,8 @@ static inline void check_heap_object(const void *ptr, unsigned long n,
		/* Check slab allocator for flags and size. */
		__check_heap_object(ptr, n, folio_slab(folio), to_user);
	} else if (folio_test_large(folio)) {
		unsigned long offset = ptr - folio_address(folio);
		if (offset + n > folio_size(folio))
		offset = ptr - folio_address(folio);
		if (n > folio_size(folio) - offset)
			usercopy_abort("page alloc", NULL, to_user, offset, n);
	}
}