Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

mm/usercopy: Check kmap addresses properly

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

authored by

Matthew Wilcox (Oracle) and committed by
Kees Cook
4e140f59 a1994480

+21 -6
+1
arch/x86/include/asm/highmem.h
··· 26 26 #include <asm/tlbflush.h> 27 27 #include <asm/paravirt.h> 28 28 #include <asm/fixmap.h> 29 + #include <asm/pgtable_areas.h> 29 30 30 31 /* declarations for highmem.c */ 31 32 extern unsigned long highstart_pfn, highend_pfn;
+10
include/linux/highmem-internal.h
··· 149 149 atomic_long_add(count, &_totalhigh_pages); 150 150 } 151 151 152 + static inline bool is_kmap_addr(const void *x) 153 + { 154 + unsigned long addr = (unsigned long)x; 155 + return addr >= PKMAP_ADDR(0) && addr < PKMAP_ADDR(LAST_PKMAP); 156 + } 152 157 #else /* CONFIG_HIGHMEM */ 153 158 154 159 static inline struct page *kmap_to_page(void *addr) ··· 238 233 239 234 static inline unsigned int nr_free_highpages(void) { return 0; } 240 235 static inline unsigned long totalhigh_pages(void) { return 0UL; } 236 + 237 + static inline bool is_kmap_addr(const void *x) 238 + { 239 + return false; 240 + } 241 241 242 242 #endif /* CONFIG_HIGHMEM */ 243 243
+10 -6
mm/usercopy.c
··· 229 229 if (!virt_addr_valid(ptr)) 230 230 return; 231 231 232 - /* 233 - * When CONFIG_HIGHMEM=y, kmap_to_page() will give either the 234 - * highmem page or fallback to virt_to_page(). The following 235 - * is effectively a highmem-aware virt_to_slab(). 236 - */ 237 - folio = page_folio(kmap_to_page((void *)ptr)); 232 + if (is_kmap_addr(ptr)) { 233 + unsigned long page_end = (unsigned long)ptr | (PAGE_SIZE - 1); 234 + 235 + if ((unsigned long)ptr + n - 1 > page_end) 236 + usercopy_abort("kmap", NULL, to_user, 237 + offset_in_page(ptr), n); 238 + return; 239 + } 240 + 241 + folio = virt_to_folio(ptr); 238 242 239 243 if (folio_test_slab(folio)) { 240 244 /* Check slab allocator for flags and size. */