Skip to content

Commit

Permalink
mm/usercopy: Check kmap addresses properly
Browse files Browse the repository at this point in the history
If you are copying to an address in the kmap region, you may not copy
across a page boundary, no matter what the size of the underlying
allocation.  You can't kmap() a slab page because slab pages always
come from low memory.

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-2-willy@infradead.org
  • Loading branch information
Matthew Wilcox (Oracle) authored and Kees Cook committed Apr 13, 2022
1 parent a199448 commit 4e140f5
Show file tree
Hide file tree
Showing 3 changed files with 21 additions and 6 deletions.
1 change: 1 addition & 0 deletions arch/x86/include/asm/highmem.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include <asm/tlbflush.h>
#include <asm/paravirt.h>
#include <asm/fixmap.h>
#include <asm/pgtable_areas.h>

/* declarations for highmem.c */
extern unsigned long highstart_pfn, highend_pfn;
Expand Down
10 changes: 10 additions & 0 deletions include/linux/highmem-internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,11 @@ static inline void totalhigh_pages_add(long count)
atomic_long_add(count, &_totalhigh_pages);
}

static inline bool is_kmap_addr(const void *x)
{
unsigned long addr = (unsigned long)x;
return addr >= PKMAP_ADDR(0) && addr < PKMAP_ADDR(LAST_PKMAP);
}
#else /* CONFIG_HIGHMEM */

static inline struct page *kmap_to_page(void *addr)
Expand Down Expand Up @@ -234,6 +239,11 @@ static inline void __kunmap_atomic(void *addr)
static inline unsigned int nr_free_highpages(void) { return 0; }
static inline unsigned long totalhigh_pages(void) { return 0UL; }

static inline bool is_kmap_addr(const void *x)
{
return false;
}

#endif /* CONFIG_HIGHMEM */

/*
Expand Down
16 changes: 10 additions & 6 deletions mm/usercopy.c
Original file line number Diff line number Diff line change
Expand Up @@ -229,12 +229,16 @@ static inline void check_heap_object(const void *ptr, unsigned long n,
if (!virt_addr_valid(ptr))
return;

/*
* When CONFIG_HIGHMEM=y, kmap_to_page() will give either the
* highmem page or fallback to virt_to_page(). The following
* is effectively a highmem-aware virt_to_slab().
*/
folio = page_folio(kmap_to_page((void *)ptr));
if (is_kmap_addr(ptr)) {
unsigned long page_end = (unsigned long)ptr | (PAGE_SIZE - 1);

if ((unsigned long)ptr + n - 1 > page_end)
usercopy_abort("kmap", NULL, to_user,
offset_in_page(ptr), n);
return;
}

folio = virt_to_folio(ptr);

if (folio_test_slab(folio)) {
/* Check slab allocator for flags and size. */
Expand Down

0 comments on commit 4e140f5

Please sign in to comment.