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

lkdtm/usercopy: Check vmalloc and >0-order folios

Add coverage for the recently added usercopy checks for vmalloc and
folios, via USERCOPY_VMALLOC and USERCOPY_FOLIO respectively.

Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Matthew Wilcox (Oracle) <willy@infradead.org>
Cc: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Kees Cook <keescook@chromium.org>

+83
+83
drivers/misc/lkdtm/usercopy.c
··· 5 5 */ 6 6 #include "lkdtm.h" 7 7 #include <linux/slab.h> 8 + #include <linux/highmem.h> 8 9 #include <linux/vmalloc.h> 9 10 #include <linux/sched/task_stack.h> 10 11 #include <linux/mman.h> ··· 342 341 vm_munmap(user_addr, PAGE_SIZE); 343 342 } 344 343 344 + /* 345 + * This expects "kaddr" to point to a PAGE_SIZE allocation, which means 346 + * a more complete test that would include copy_from_user() would risk 347 + * memory corruption. Just test copy_to_user() here, as that exercises 348 + * almost exactly the same code paths. 349 + */ 350 + static void do_usercopy_page_span(const char *name, void *kaddr) 351 + { 352 + unsigned long uaddr; 353 + 354 + uaddr = vm_mmap(NULL, 0, PAGE_SIZE, PROT_READ | PROT_WRITE, 355 + MAP_ANONYMOUS | MAP_PRIVATE, 0); 356 + if (uaddr >= TASK_SIZE) { 357 + pr_warn("Failed to allocate user memory\n"); 358 + return; 359 + } 360 + 361 + /* Initialize contents. */ 362 + memset(kaddr, 0xAA, PAGE_SIZE); 363 + 364 + /* Bump the kaddr forward to detect a page-spanning overflow. */ 365 + kaddr += PAGE_SIZE / 2; 366 + 367 + pr_info("attempting good copy_to_user() from kernel %s: %px\n", 368 + name, kaddr); 369 + if (copy_to_user((void __user *)uaddr, kaddr, 370 + unconst + (PAGE_SIZE / 2))) { 371 + pr_err("copy_to_user() failed unexpectedly?!\n"); 372 + goto free_user; 373 + } 374 + 375 + pr_info("attempting bad copy_to_user() from kernel %s: %px\n", 376 + name, kaddr); 377 + if (copy_to_user((void __user *)uaddr, kaddr, unconst + PAGE_SIZE)) { 378 + pr_warn("Good, copy_to_user() failed, but lacked Oops(?!)\n"); 379 + goto free_user; 380 + } 381 + 382 + pr_err("FAIL: bad copy_to_user() not detected!\n"); 383 + pr_expected_config_param(CONFIG_HARDENED_USERCOPY, "hardened_usercopy"); 384 + 385 + free_user: 386 + vm_munmap(uaddr, PAGE_SIZE); 387 + } 388 + 389 + static void lkdtm_USERCOPY_VMALLOC(void) 390 + { 391 + void *addr; 392 + 393 + addr = vmalloc(PAGE_SIZE); 394 + if (!addr) { 395 + pr_err("vmalloc() failed!?\n"); 396 + return; 397 + } 398 + do_usercopy_page_span("vmalloc", addr); 399 + vfree(addr); 400 + } 401 + 402 + static void lkdtm_USERCOPY_FOLIO(void) 403 + { 404 + struct folio *folio; 405 + void *addr; 406 + 407 + /* 408 + * FIXME: Folio checking currently misses 0-order allocations, so 409 + * allocate and bump forward to the last page. 410 + */ 411 + folio = folio_alloc(GFP_KERNEL | __GFP_ZERO, 1); 412 + if (!folio) { 413 + pr_err("folio_alloc() failed!?\n"); 414 + return; 415 + } 416 + addr = folio_address(folio); 417 + if (addr) 418 + do_usercopy_page_span("folio", addr + PAGE_SIZE); 419 + else 420 + pr_err("folio_address() failed?!\n"); 421 + folio_put(folio); 422 + } 423 + 345 424 void __init lkdtm_usercopy_init(void) 346 425 { 347 426 /* Prepare cache that lacks SLAB_USERCOPY flag. */ ··· 446 365 CRASHTYPE(USERCOPY_STACK_FRAME_TO), 447 366 CRASHTYPE(USERCOPY_STACK_FRAME_FROM), 448 367 CRASHTYPE(USERCOPY_STACK_BEYOND), 368 + CRASHTYPE(USERCOPY_VMALLOC), 369 + CRASHTYPE(USERCOPY_FOLIO), 449 370 CRASHTYPE(USERCOPY_KERNEL), 450 371 }; 451 372