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.

at v4.15-rc6 324 lines 8.4 kB view raw
1// SPDX-License-Identifier: GPL-2.0 2/* 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/vmalloc.h> 9#include <linux/sched/task_stack.h> 10#include <linux/mman.h> 11#include <linux/uaccess.h> 12#include <asm/cacheflush.h> 13 14/* 15 * Many of the tests here end up using const sizes, but those would 16 * normally be ignored by hardened usercopy, so force the compiler 17 * into choosing the non-const path to make sure we trigger the 18 * hardened usercopy checks by added "unconst" to all the const copies, 19 * and making sure "cache_size" isn't optimized into a const. 20 */ 21static volatile size_t unconst = 0; 22static volatile size_t cache_size = 1024; 23static struct kmem_cache *bad_cache; 24 25static const unsigned char test_text[] = "This is a test.\n"; 26 27/* 28 * Instead of adding -Wno-return-local-addr, just pass the stack address 29 * through a function to obfuscate it from the compiler. 30 */ 31static noinline unsigned char *trick_compiler(unsigned char *stack) 32{ 33 return stack + 0; 34} 35 36static noinline unsigned char *do_usercopy_stack_callee(int value) 37{ 38 unsigned char buf[32]; 39 int i; 40 41 /* Exercise stack to avoid everything living in registers. */ 42 for (i = 0; i < sizeof(buf); i++) { 43 buf[i] = value & 0xff; 44 } 45 46 return trick_compiler(buf); 47} 48 49static noinline void do_usercopy_stack(bool to_user, bool bad_frame) 50{ 51 unsigned long user_addr; 52 unsigned char good_stack[32]; 53 unsigned char *bad_stack; 54 int i; 55 56 /* Exercise stack to avoid everything living in registers. */ 57 for (i = 0; i < sizeof(good_stack); i++) 58 good_stack[i] = test_text[i % sizeof(test_text)]; 59 60 /* This is a pointer to outside our current stack frame. */ 61 if (bad_frame) { 62 bad_stack = do_usercopy_stack_callee((uintptr_t)&bad_stack); 63 } else { 64 /* Put start address just inside stack. */ 65 bad_stack = task_stack_page(current) + THREAD_SIZE; 66 bad_stack -= sizeof(unsigned long); 67 } 68 69 user_addr = vm_mmap(NULL, 0, PAGE_SIZE, 70 PROT_READ | PROT_WRITE | PROT_EXEC, 71 MAP_ANONYMOUS | MAP_PRIVATE, 0); 72 if (user_addr >= TASK_SIZE) { 73 pr_warn("Failed to allocate user memory\n"); 74 return; 75 } 76 77 if (to_user) { 78 pr_info("attempting good copy_to_user of local stack\n"); 79 if (copy_to_user((void __user *)user_addr, good_stack, 80 unconst + sizeof(good_stack))) { 81 pr_warn("copy_to_user failed unexpectedly?!\n"); 82 goto free_user; 83 } 84 85 pr_info("attempting bad copy_to_user of distant stack\n"); 86 if (copy_to_user((void __user *)user_addr, bad_stack, 87 unconst + sizeof(good_stack))) { 88 pr_warn("copy_to_user failed, but lacked Oops\n"); 89 goto free_user; 90 } 91 } else { 92 /* 93 * There isn't a safe way to not be protected by usercopy 94 * if we're going to write to another thread's stack. 95 */ 96 if (!bad_frame) 97 goto free_user; 98 99 pr_info("attempting good copy_from_user of local stack\n"); 100 if (copy_from_user(good_stack, (void __user *)user_addr, 101 unconst + sizeof(good_stack))) { 102 pr_warn("copy_from_user failed unexpectedly?!\n"); 103 goto free_user; 104 } 105 106 pr_info("attempting bad copy_from_user of distant stack\n"); 107 if (copy_from_user(bad_stack, (void __user *)user_addr, 108 unconst + sizeof(good_stack))) { 109 pr_warn("copy_from_user failed, but lacked Oops\n"); 110 goto free_user; 111 } 112 } 113 114free_user: 115 vm_munmap(user_addr, PAGE_SIZE); 116} 117 118static void do_usercopy_heap_size(bool to_user) 119{ 120 unsigned long user_addr; 121 unsigned char *one, *two; 122 size_t size = unconst + 1024; 123 124 one = kmalloc(size, GFP_KERNEL); 125 two = kmalloc(size, GFP_KERNEL); 126 if (!one || !two) { 127 pr_warn("Failed to allocate kernel memory\n"); 128 goto free_kernel; 129 } 130 131 user_addr = vm_mmap(NULL, 0, PAGE_SIZE, 132 PROT_READ | PROT_WRITE | PROT_EXEC, 133 MAP_ANONYMOUS | MAP_PRIVATE, 0); 134 if (user_addr >= TASK_SIZE) { 135 pr_warn("Failed to allocate user memory\n"); 136 goto free_kernel; 137 } 138 139 memset(one, 'A', size); 140 memset(two, 'B', size); 141 142 if (to_user) { 143 pr_info("attempting good copy_to_user of correct size\n"); 144 if (copy_to_user((void __user *)user_addr, one, size)) { 145 pr_warn("copy_to_user failed unexpectedly?!\n"); 146 goto free_user; 147 } 148 149 pr_info("attempting bad copy_to_user of too large size\n"); 150 if (copy_to_user((void __user *)user_addr, one, 2 * size)) { 151 pr_warn("copy_to_user failed, but lacked Oops\n"); 152 goto free_user; 153 } 154 } else { 155 pr_info("attempting good copy_from_user of correct size\n"); 156 if (copy_from_user(one, (void __user *)user_addr, size)) { 157 pr_warn("copy_from_user failed unexpectedly?!\n"); 158 goto free_user; 159 } 160 161 pr_info("attempting bad copy_from_user of too large size\n"); 162 if (copy_from_user(one, (void __user *)user_addr, 2 * size)) { 163 pr_warn("copy_from_user failed, but lacked Oops\n"); 164 goto free_user; 165 } 166 } 167 168free_user: 169 vm_munmap(user_addr, PAGE_SIZE); 170free_kernel: 171 kfree(one); 172 kfree(two); 173} 174 175static void do_usercopy_heap_flag(bool to_user) 176{ 177 unsigned long user_addr; 178 unsigned char *good_buf = NULL; 179 unsigned char *bad_buf = NULL; 180 181 /* Make sure cache was prepared. */ 182 if (!bad_cache) { 183 pr_warn("Failed to allocate kernel cache\n"); 184 return; 185 } 186 187 /* 188 * Allocate one buffer from each cache (kmalloc will have the 189 * SLAB_USERCOPY flag already, but "bad_cache" won't). 190 */ 191 good_buf = kmalloc(cache_size, GFP_KERNEL); 192 bad_buf = kmem_cache_alloc(bad_cache, GFP_KERNEL); 193 if (!good_buf || !bad_buf) { 194 pr_warn("Failed to allocate buffers from caches\n"); 195 goto free_alloc; 196 } 197 198 /* Allocate user memory we'll poke at. */ 199 user_addr = vm_mmap(NULL, 0, PAGE_SIZE, 200 PROT_READ | PROT_WRITE | PROT_EXEC, 201 MAP_ANONYMOUS | MAP_PRIVATE, 0); 202 if (user_addr >= TASK_SIZE) { 203 pr_warn("Failed to allocate user memory\n"); 204 goto free_alloc; 205 } 206 207 memset(good_buf, 'A', cache_size); 208 memset(bad_buf, 'B', cache_size); 209 210 if (to_user) { 211 pr_info("attempting good copy_to_user with SLAB_USERCOPY\n"); 212 if (copy_to_user((void __user *)user_addr, good_buf, 213 cache_size)) { 214 pr_warn("copy_to_user failed unexpectedly?!\n"); 215 goto free_user; 216 } 217 218 pr_info("attempting bad copy_to_user w/o SLAB_USERCOPY\n"); 219 if (copy_to_user((void __user *)user_addr, bad_buf, 220 cache_size)) { 221 pr_warn("copy_to_user failed, but lacked Oops\n"); 222 goto free_user; 223 } 224 } else { 225 pr_info("attempting good copy_from_user with SLAB_USERCOPY\n"); 226 if (copy_from_user(good_buf, (void __user *)user_addr, 227 cache_size)) { 228 pr_warn("copy_from_user failed unexpectedly?!\n"); 229 goto free_user; 230 } 231 232 pr_info("attempting bad copy_from_user w/o SLAB_USERCOPY\n"); 233 if (copy_from_user(bad_buf, (void __user *)user_addr, 234 cache_size)) { 235 pr_warn("copy_from_user failed, but lacked Oops\n"); 236 goto free_user; 237 } 238 } 239 240free_user: 241 vm_munmap(user_addr, PAGE_SIZE); 242free_alloc: 243 if (bad_buf) 244 kmem_cache_free(bad_cache, bad_buf); 245 kfree(good_buf); 246} 247 248/* Callable tests. */ 249void lkdtm_USERCOPY_HEAP_SIZE_TO(void) 250{ 251 do_usercopy_heap_size(true); 252} 253 254void lkdtm_USERCOPY_HEAP_SIZE_FROM(void) 255{ 256 do_usercopy_heap_size(false); 257} 258 259void lkdtm_USERCOPY_HEAP_FLAG_TO(void) 260{ 261 do_usercopy_heap_flag(true); 262} 263 264void lkdtm_USERCOPY_HEAP_FLAG_FROM(void) 265{ 266 do_usercopy_heap_flag(false); 267} 268 269void lkdtm_USERCOPY_STACK_FRAME_TO(void) 270{ 271 do_usercopy_stack(true, true); 272} 273 274void lkdtm_USERCOPY_STACK_FRAME_FROM(void) 275{ 276 do_usercopy_stack(false, true); 277} 278 279void lkdtm_USERCOPY_STACK_BEYOND(void) 280{ 281 do_usercopy_stack(true, false); 282} 283 284void lkdtm_USERCOPY_KERNEL(void) 285{ 286 unsigned long user_addr; 287 288 user_addr = vm_mmap(NULL, 0, PAGE_SIZE, 289 PROT_READ | PROT_WRITE | PROT_EXEC, 290 MAP_ANONYMOUS | MAP_PRIVATE, 0); 291 if (user_addr >= TASK_SIZE) { 292 pr_warn("Failed to allocate user memory\n"); 293 return; 294 } 295 296 pr_info("attempting good copy_to_user from kernel rodata\n"); 297 if (copy_to_user((void __user *)user_addr, test_text, 298 unconst + sizeof(test_text))) { 299 pr_warn("copy_to_user failed unexpectedly?!\n"); 300 goto free_user; 301 } 302 303 pr_info("attempting bad copy_to_user from kernel text\n"); 304 if (copy_to_user((void __user *)user_addr, vm_mmap, 305 unconst + PAGE_SIZE)) { 306 pr_warn("copy_to_user failed, but lacked Oops\n"); 307 goto free_user; 308 } 309 310free_user: 311 vm_munmap(user_addr, PAGE_SIZE); 312} 313 314void __init lkdtm_usercopy_init(void) 315{ 316 /* Prepare cache that lacks SLAB_USERCOPY flag. */ 317 bad_cache = kmem_cache_create("lkdtm-no-usercopy", cache_size, 0, 318 0, NULL); 319} 320 321void __exit lkdtm_usercopy_exit(void) 322{ 323 kmem_cache_destroy(bad_cache); 324}