458 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * This is for all the tests related to copy_to_user() and copy_from_user()4 * hardening.5 */6#include "lkdtm.h"7#include <linux/slab.h>8#include <linux/highmem.h>9#include <linux/vmalloc.h>10#include <linux/sched/task_stack.h>11#include <linux/mman.h>12#include <linux/uaccess.h>13#include <asm/cacheflush.h>14 15/*16 * Many of the tests here end up using const sizes, but those would17 * normally be ignored by hardened usercopy, so force the compiler18 * into choosing the non-const path to make sure we trigger the19 * hardened usercopy checks by added "unconst" to all the const copies,20 * and making sure "cache_size" isn't optimized into a const.21 */22static volatile size_t unconst;23static volatile size_t cache_size = 1024;24static struct kmem_cache *whitelist_cache;25 26static const unsigned char test_text[] = "This is a test.\n";27 28/*29 * Instead of adding -Wno-return-local-addr, just pass the stack address30 * through a function to obfuscate it from the compiler.31 */32static noinline unsigned char *trick_compiler(unsigned char *stack)33{34 return stack + unconst;35}36 37static noinline unsigned char *do_usercopy_stack_callee(int value)38{39 unsigned char buf[128];40 int i;41 42 /* Exercise stack to avoid everything living in registers. */43 for (i = 0; i < sizeof(buf); i++) {44 buf[i] = value & 0xff;45 }46 47 /*48 * Put the target buffer in the middle of stack allocation49 * so that we don't step on future stack users regardless50 * of stack growth direction.51 */52 return trick_compiler(&buf[(128/2)-32]);53}54 55static noinline void do_usercopy_stack(bool to_user, bool bad_frame)56{57 unsigned long user_addr;58 unsigned char good_stack[32];59 unsigned char *bad_stack;60 int i;61 62 /* Exercise stack to avoid everything living in registers. */63 for (i = 0; i < sizeof(good_stack); i++)64 good_stack[i] = test_text[i % sizeof(test_text)];65 66 /* This is a pointer to outside our current stack frame. */67 if (bad_frame) {68 bad_stack = do_usercopy_stack_callee((uintptr_t)&bad_stack);69 } else {70 /* Put start address just inside stack. */71 bad_stack = task_stack_page(current) + THREAD_SIZE;72 bad_stack -= sizeof(unsigned long);73 }74 75#ifdef ARCH_HAS_CURRENT_STACK_POINTER76 pr_info("stack : %px\n", (void *)current_stack_pointer);77#endif78 pr_info("good_stack: %px-%px\n", good_stack, good_stack + sizeof(good_stack));79 pr_info("bad_stack : %px-%px\n", bad_stack, bad_stack + sizeof(good_stack));80 81 user_addr = vm_mmap(NULL, 0, PAGE_SIZE,82 PROT_READ | PROT_WRITE | PROT_EXEC,83 MAP_ANONYMOUS | MAP_PRIVATE, 0);84 if (user_addr >= TASK_SIZE) {85 pr_warn("Failed to allocate user memory\n");86 return;87 }88 89 if (to_user) {90 pr_info("attempting good copy_to_user of local stack\n");91 if (copy_to_user((void __user *)user_addr, good_stack,92 unconst + sizeof(good_stack))) {93 pr_warn("copy_to_user failed unexpectedly?!\n");94 goto free_user;95 }96 97 pr_info("attempting bad copy_to_user of distant stack\n");98 if (copy_to_user((void __user *)user_addr, bad_stack,99 unconst + sizeof(good_stack))) {100 pr_warn("copy_to_user failed, but lacked Oops\n");101 goto free_user;102 }103 } else {104 /*105 * There isn't a safe way to not be protected by usercopy106 * if we're going to write to another thread's stack.107 */108 if (!bad_frame)109 goto free_user;110 111 pr_info("attempting good copy_from_user of local stack\n");112 if (copy_from_user(good_stack, (void __user *)user_addr,113 unconst + sizeof(good_stack))) {114 pr_warn("copy_from_user failed unexpectedly?!\n");115 goto free_user;116 }117 118 pr_info("attempting bad copy_from_user of distant stack\n");119 if (copy_from_user(bad_stack, (void __user *)user_addr,120 unconst + sizeof(good_stack))) {121 pr_warn("copy_from_user failed, but lacked Oops\n");122 goto free_user;123 }124 }125 126free_user:127 vm_munmap(user_addr, PAGE_SIZE);128}129 130/*131 * This checks for whole-object size validation with hardened usercopy,132 * with or without usercopy whitelisting.133 */134static void do_usercopy_slab_size(bool to_user)135{136 unsigned long user_addr;137 unsigned char *one, *two;138 void __user *test_user_addr;139 void *test_kern_addr;140 size_t size = unconst + 1024;141 142 one = kmalloc(size, GFP_KERNEL);143 two = kmalloc(size, GFP_KERNEL);144 if (!one || !two) {145 pr_warn("Failed to allocate kernel memory\n");146 goto free_kernel;147 }148 149 user_addr = vm_mmap(NULL, 0, PAGE_SIZE,150 PROT_READ | PROT_WRITE | PROT_EXEC,151 MAP_ANONYMOUS | MAP_PRIVATE, 0);152 if (user_addr >= TASK_SIZE) {153 pr_warn("Failed to allocate user memory\n");154 goto free_kernel;155 }156 157 memset(one, 'A', size);158 memset(two, 'B', size);159 160 test_user_addr = (void __user *)(user_addr + 16);161 test_kern_addr = one + 16;162 163 if (to_user) {164 pr_info("attempting good copy_to_user of correct size\n");165 if (copy_to_user(test_user_addr, test_kern_addr, size / 2)) {166 pr_warn("copy_to_user failed unexpectedly?!\n");167 goto free_user;168 }169 170 pr_info("attempting bad copy_to_user of too large size\n");171 if (copy_to_user(test_user_addr, test_kern_addr, size)) {172 pr_warn("copy_to_user failed, but lacked Oops\n");173 goto free_user;174 }175 } else {176 pr_info("attempting good copy_from_user of correct size\n");177 if (copy_from_user(test_kern_addr, test_user_addr, size / 2)) {178 pr_warn("copy_from_user failed unexpectedly?!\n");179 goto free_user;180 }181 182 pr_info("attempting bad copy_from_user of too large size\n");183 if (copy_from_user(test_kern_addr, test_user_addr, size)) {184 pr_warn("copy_from_user failed, but lacked Oops\n");185 goto free_user;186 }187 }188 pr_err("FAIL: bad usercopy not detected!\n");189 pr_expected_config_param(CONFIG_HARDENED_USERCOPY, "hardened_usercopy");190 191free_user:192 vm_munmap(user_addr, PAGE_SIZE);193free_kernel:194 kfree(one);195 kfree(two);196}197 198/*199 * This checks for the specific whitelist window within an object. If this200 * test passes, then do_usercopy_slab_size() tests will pass too.201 */202static void do_usercopy_slab_whitelist(bool to_user)203{204 unsigned long user_alloc;205 unsigned char *buf = NULL;206 unsigned char __user *user_addr;207 size_t offset, size;208 209 /* Make sure cache was prepared. */210 if (!whitelist_cache) {211 pr_warn("Failed to allocate kernel cache\n");212 return;213 }214 215 /*216 * Allocate a buffer with a whitelisted window in the buffer.217 */218 buf = kmem_cache_alloc(whitelist_cache, GFP_KERNEL);219 if (!buf) {220 pr_warn("Failed to allocate buffer from whitelist cache\n");221 goto free_alloc;222 }223 224 /* Allocate user memory we'll poke at. */225 user_alloc = vm_mmap(NULL, 0, PAGE_SIZE,226 PROT_READ | PROT_WRITE | PROT_EXEC,227 MAP_ANONYMOUS | MAP_PRIVATE, 0);228 if (user_alloc >= TASK_SIZE) {229 pr_warn("Failed to allocate user memory\n");230 goto free_alloc;231 }232 user_addr = (void __user *)user_alloc;233 234 memset(buf, 'B', cache_size);235 236 /* Whitelisted window in buffer, from kmem_cache_create_usercopy. */237 offset = (cache_size / 4) + unconst;238 size = (cache_size / 16) + unconst;239 240 if (to_user) {241 pr_info("attempting good copy_to_user inside whitelist\n");242 if (copy_to_user(user_addr, buf + offset, size)) {243 pr_warn("copy_to_user failed unexpectedly?!\n");244 goto free_user;245 }246 247 pr_info("attempting bad copy_to_user outside whitelist\n");248 if (copy_to_user(user_addr, buf + offset - 1, size)) {249 pr_warn("copy_to_user failed, but lacked Oops\n");250 goto free_user;251 }252 } else {253 pr_info("attempting good copy_from_user inside whitelist\n");254 if (copy_from_user(buf + offset, user_addr, size)) {255 pr_warn("copy_from_user failed unexpectedly?!\n");256 goto free_user;257 }258 259 pr_info("attempting bad copy_from_user outside whitelist\n");260 if (copy_from_user(buf + offset - 1, user_addr, size)) {261 pr_warn("copy_from_user failed, but lacked Oops\n");262 goto free_user;263 }264 }265 pr_err("FAIL: bad usercopy not detected!\n");266 pr_expected_config_param(CONFIG_HARDENED_USERCOPY, "hardened_usercopy");267 268free_user:269 vm_munmap(user_alloc, PAGE_SIZE);270free_alloc:271 if (buf)272 kmem_cache_free(whitelist_cache, buf);273}274 275/* Callable tests. */276static void lkdtm_USERCOPY_SLAB_SIZE_TO(void)277{278 do_usercopy_slab_size(true);279}280 281static void lkdtm_USERCOPY_SLAB_SIZE_FROM(void)282{283 do_usercopy_slab_size(false);284}285 286static void lkdtm_USERCOPY_SLAB_WHITELIST_TO(void)287{288 do_usercopy_slab_whitelist(true);289}290 291static void lkdtm_USERCOPY_SLAB_WHITELIST_FROM(void)292{293 do_usercopy_slab_whitelist(false);294}295 296static void lkdtm_USERCOPY_STACK_FRAME_TO(void)297{298 do_usercopy_stack(true, true);299}300 301static void lkdtm_USERCOPY_STACK_FRAME_FROM(void)302{303 do_usercopy_stack(false, true);304}305 306static void lkdtm_USERCOPY_STACK_BEYOND(void)307{308 do_usercopy_stack(true, false);309}310 311static void lkdtm_USERCOPY_KERNEL(void)312{313 unsigned long user_addr;314 315 user_addr = vm_mmap(NULL, 0, PAGE_SIZE,316 PROT_READ | PROT_WRITE | PROT_EXEC,317 MAP_ANONYMOUS | MAP_PRIVATE, 0);318 if (user_addr >= TASK_SIZE) {319 pr_warn("Failed to allocate user memory\n");320 return;321 }322 323 pr_info("attempting good copy_to_user from kernel rodata: %px\n",324 test_text);325 if (copy_to_user((void __user *)user_addr, test_text,326 unconst + sizeof(test_text))) {327 pr_warn("copy_to_user failed unexpectedly?!\n");328 goto free_user;329 }330 331 pr_info("attempting bad copy_to_user from kernel text: %px\n",332 vm_mmap);333 if (copy_to_user((void __user *)user_addr, vm_mmap,334 unconst + PAGE_SIZE)) {335 pr_warn("copy_to_user failed, but lacked Oops\n");336 goto free_user;337 }338 pr_err("FAIL: bad copy_to_user() not detected!\n");339 pr_expected_config_param(CONFIG_HARDENED_USERCOPY, "hardened_usercopy");340 341free_user:342 vm_munmap(user_addr, PAGE_SIZE);343}344 345/*346 * This expects "kaddr" to point to a PAGE_SIZE allocation, which means347 * a more complete test that would include copy_from_user() would risk348 * memory corruption. Just test copy_to_user() here, as that exercises349 * almost exactly the same code paths.350 */351static void do_usercopy_page_span(const char *name, void *kaddr)352{353 unsigned long uaddr;354 355 uaddr = vm_mmap(NULL, 0, PAGE_SIZE, PROT_READ | PROT_WRITE,356 MAP_ANONYMOUS | MAP_PRIVATE, 0);357 if (uaddr >= TASK_SIZE) {358 pr_warn("Failed to allocate user memory\n");359 return;360 }361 362 /* Initialize contents. */363 memset(kaddr, 0xAA, PAGE_SIZE);364 365 /* Bump the kaddr forward to detect a page-spanning overflow. */366 kaddr += PAGE_SIZE / 2;367 368 pr_info("attempting good copy_to_user() from kernel %s: %px\n",369 name, kaddr);370 if (copy_to_user((void __user *)uaddr, kaddr,371 unconst + (PAGE_SIZE / 2))) {372 pr_err("copy_to_user() failed unexpectedly?!\n");373 goto free_user;374 }375 376 pr_info("attempting bad copy_to_user() from kernel %s: %px\n",377 name, kaddr);378 if (copy_to_user((void __user *)uaddr, kaddr, unconst + PAGE_SIZE)) {379 pr_warn("Good, copy_to_user() failed, but lacked Oops(?!)\n");380 goto free_user;381 }382 383 pr_err("FAIL: bad copy_to_user() not detected!\n");384 pr_expected_config_param(CONFIG_HARDENED_USERCOPY, "hardened_usercopy");385 386free_user:387 vm_munmap(uaddr, PAGE_SIZE);388}389 390static void lkdtm_USERCOPY_VMALLOC(void)391{392 void *addr;393 394 addr = vmalloc(PAGE_SIZE);395 if (!addr) {396 pr_err("vmalloc() failed!?\n");397 return;398 }399 do_usercopy_page_span("vmalloc", addr);400 vfree(addr);401}402 403static void lkdtm_USERCOPY_FOLIO(void)404{405 struct folio *folio;406 void *addr;407 408 /*409 * FIXME: Folio checking currently misses 0-order allocations, so410 * allocate and bump forward to the last page.411 */412 folio = folio_alloc(GFP_KERNEL | __GFP_ZERO, 1);413 if (!folio) {414 pr_err("folio_alloc() failed!?\n");415 return;416 }417 addr = folio_address(folio);418 if (addr)419 do_usercopy_page_span("folio", addr + PAGE_SIZE);420 else421 pr_err("folio_address() failed?!\n");422 folio_put(folio);423}424 425void __init lkdtm_usercopy_init(void)426{427 /* Prepare cache that lacks SLAB_USERCOPY flag. */428 whitelist_cache =429 kmem_cache_create_usercopy("lkdtm-usercopy", cache_size,430 0, 0,431 cache_size / 4,432 cache_size / 16,433 NULL);434}435 436void __exit lkdtm_usercopy_exit(void)437{438 kmem_cache_destroy(whitelist_cache);439}440 441static struct crashtype crashtypes[] = {442 CRASHTYPE(USERCOPY_SLAB_SIZE_TO),443 CRASHTYPE(USERCOPY_SLAB_SIZE_FROM),444 CRASHTYPE(USERCOPY_SLAB_WHITELIST_TO),445 CRASHTYPE(USERCOPY_SLAB_WHITELIST_FROM),446 CRASHTYPE(USERCOPY_STACK_FRAME_TO),447 CRASHTYPE(USERCOPY_STACK_FRAME_FROM),448 CRASHTYPE(USERCOPY_STACK_BEYOND),449 CRASHTYPE(USERCOPY_VMALLOC),450 CRASHTYPE(USERCOPY_FOLIO),451 CRASHTYPE(USERCOPY_KERNEL),452};453 454struct crashtype_category usercopy_crashtypes = {455 .crashtypes = crashtypes,456 .len = ARRAY_SIZE(crashtypes),457};458