at v4.12 10 kB view raw
1/* 2 * 3 * Copyright (c) 2014 Samsung Electronics Co., Ltd. 4 * Author: Andrey Ryabinin <a.ryabinin@samsung.com> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 2 as 8 * published by the Free Software Foundation. 9 * 10 */ 11 12#define pr_fmt(fmt) "kasan test: %s " fmt, __func__ 13 14#include <linux/delay.h> 15#include <linux/kernel.h> 16#include <linux/mman.h> 17#include <linux/mm.h> 18#include <linux/printk.h> 19#include <linux/slab.h> 20#include <linux/string.h> 21#include <linux/uaccess.h> 22#include <linux/module.h> 23#include <linux/kasan.h> 24 25/* 26 * Note: test functions are marked noinline so that their names appear in 27 * reports. 28 */ 29 30static noinline void __init kmalloc_oob_right(void) 31{ 32 char *ptr; 33 size_t size = 123; 34 35 pr_info("out-of-bounds to right\n"); 36 ptr = kmalloc(size, GFP_KERNEL); 37 if (!ptr) { 38 pr_err("Allocation failed\n"); 39 return; 40 } 41 42 ptr[size] = 'x'; 43 kfree(ptr); 44} 45 46static noinline void __init kmalloc_oob_left(void) 47{ 48 char *ptr; 49 size_t size = 15; 50 51 pr_info("out-of-bounds to left\n"); 52 ptr = kmalloc(size, GFP_KERNEL); 53 if (!ptr) { 54 pr_err("Allocation failed\n"); 55 return; 56 } 57 58 *ptr = *(ptr - 1); 59 kfree(ptr); 60} 61 62static noinline void __init kmalloc_node_oob_right(void) 63{ 64 char *ptr; 65 size_t size = 4096; 66 67 pr_info("kmalloc_node(): out-of-bounds to right\n"); 68 ptr = kmalloc_node(size, GFP_KERNEL, 0); 69 if (!ptr) { 70 pr_err("Allocation failed\n"); 71 return; 72 } 73 74 ptr[size] = 0; 75 kfree(ptr); 76} 77 78#ifdef CONFIG_SLUB 79static noinline void __init kmalloc_pagealloc_oob_right(void) 80{ 81 char *ptr; 82 size_t size = KMALLOC_MAX_CACHE_SIZE + 10; 83 84 /* Allocate a chunk that does not fit into a SLUB cache to trigger 85 * the page allocator fallback. 86 */ 87 pr_info("kmalloc pagealloc allocation: out-of-bounds to right\n"); 88 ptr = kmalloc(size, GFP_KERNEL); 89 if (!ptr) { 90 pr_err("Allocation failed\n"); 91 return; 92 } 93 94 ptr[size] = 0; 95 kfree(ptr); 96} 97#endif 98 99static noinline void __init kmalloc_large_oob_right(void) 100{ 101 char *ptr; 102 size_t size = KMALLOC_MAX_CACHE_SIZE - 256; 103 /* Allocate a chunk that is large enough, but still fits into a slab 104 * and does not trigger the page allocator fallback in SLUB. 105 */ 106 pr_info("kmalloc large allocation: out-of-bounds to right\n"); 107 ptr = kmalloc(size, GFP_KERNEL); 108 if (!ptr) { 109 pr_err("Allocation failed\n"); 110 return; 111 } 112 113 ptr[size] = 0; 114 kfree(ptr); 115} 116 117static noinline void __init kmalloc_oob_krealloc_more(void) 118{ 119 char *ptr1, *ptr2; 120 size_t size1 = 17; 121 size_t size2 = 19; 122 123 pr_info("out-of-bounds after krealloc more\n"); 124 ptr1 = kmalloc(size1, GFP_KERNEL); 125 ptr2 = krealloc(ptr1, size2, GFP_KERNEL); 126 if (!ptr1 || !ptr2) { 127 pr_err("Allocation failed\n"); 128 kfree(ptr1); 129 return; 130 } 131 132 ptr2[size2] = 'x'; 133 kfree(ptr2); 134} 135 136static noinline void __init kmalloc_oob_krealloc_less(void) 137{ 138 char *ptr1, *ptr2; 139 size_t size1 = 17; 140 size_t size2 = 15; 141 142 pr_info("out-of-bounds after krealloc less\n"); 143 ptr1 = kmalloc(size1, GFP_KERNEL); 144 ptr2 = krealloc(ptr1, size2, GFP_KERNEL); 145 if (!ptr1 || !ptr2) { 146 pr_err("Allocation failed\n"); 147 kfree(ptr1); 148 return; 149 } 150 ptr2[size2] = 'x'; 151 kfree(ptr2); 152} 153 154static noinline void __init kmalloc_oob_16(void) 155{ 156 struct { 157 u64 words[2]; 158 } *ptr1, *ptr2; 159 160 pr_info("kmalloc out-of-bounds for 16-bytes access\n"); 161 ptr1 = kmalloc(sizeof(*ptr1) - 3, GFP_KERNEL); 162 ptr2 = kmalloc(sizeof(*ptr2), GFP_KERNEL); 163 if (!ptr1 || !ptr2) { 164 pr_err("Allocation failed\n"); 165 kfree(ptr1); 166 kfree(ptr2); 167 return; 168 } 169 *ptr1 = *ptr2; 170 kfree(ptr1); 171 kfree(ptr2); 172} 173 174static noinline void __init kmalloc_oob_memset_2(void) 175{ 176 char *ptr; 177 size_t size = 8; 178 179 pr_info("out-of-bounds in memset2\n"); 180 ptr = kmalloc(size, GFP_KERNEL); 181 if (!ptr) { 182 pr_err("Allocation failed\n"); 183 return; 184 } 185 186 memset(ptr+7, 0, 2); 187 kfree(ptr); 188} 189 190static noinline void __init kmalloc_oob_memset_4(void) 191{ 192 char *ptr; 193 size_t size = 8; 194 195 pr_info("out-of-bounds in memset4\n"); 196 ptr = kmalloc(size, GFP_KERNEL); 197 if (!ptr) { 198 pr_err("Allocation failed\n"); 199 return; 200 } 201 202 memset(ptr+5, 0, 4); 203 kfree(ptr); 204} 205 206 207static noinline void __init kmalloc_oob_memset_8(void) 208{ 209 char *ptr; 210 size_t size = 8; 211 212 pr_info("out-of-bounds in memset8\n"); 213 ptr = kmalloc(size, GFP_KERNEL); 214 if (!ptr) { 215 pr_err("Allocation failed\n"); 216 return; 217 } 218 219 memset(ptr+1, 0, 8); 220 kfree(ptr); 221} 222 223static noinline void __init kmalloc_oob_memset_16(void) 224{ 225 char *ptr; 226 size_t size = 16; 227 228 pr_info("out-of-bounds in memset16\n"); 229 ptr = kmalloc(size, GFP_KERNEL); 230 if (!ptr) { 231 pr_err("Allocation failed\n"); 232 return; 233 } 234 235 memset(ptr+1, 0, 16); 236 kfree(ptr); 237} 238 239static noinline void __init kmalloc_oob_in_memset(void) 240{ 241 char *ptr; 242 size_t size = 666; 243 244 pr_info("out-of-bounds in memset\n"); 245 ptr = kmalloc(size, GFP_KERNEL); 246 if (!ptr) { 247 pr_err("Allocation failed\n"); 248 return; 249 } 250 251 memset(ptr, 0, size+5); 252 kfree(ptr); 253} 254 255static noinline void __init kmalloc_uaf(void) 256{ 257 char *ptr; 258 size_t size = 10; 259 260 pr_info("use-after-free\n"); 261 ptr = kmalloc(size, GFP_KERNEL); 262 if (!ptr) { 263 pr_err("Allocation failed\n"); 264 return; 265 } 266 267 kfree(ptr); 268 *(ptr + 8) = 'x'; 269} 270 271static noinline void __init kmalloc_uaf_memset(void) 272{ 273 char *ptr; 274 size_t size = 33; 275 276 pr_info("use-after-free in memset\n"); 277 ptr = kmalloc(size, GFP_KERNEL); 278 if (!ptr) { 279 pr_err("Allocation failed\n"); 280 return; 281 } 282 283 kfree(ptr); 284 memset(ptr, 0, size); 285} 286 287static noinline void __init kmalloc_uaf2(void) 288{ 289 char *ptr1, *ptr2; 290 size_t size = 43; 291 292 pr_info("use-after-free after another kmalloc\n"); 293 ptr1 = kmalloc(size, GFP_KERNEL); 294 if (!ptr1) { 295 pr_err("Allocation failed\n"); 296 return; 297 } 298 299 kfree(ptr1); 300 ptr2 = kmalloc(size, GFP_KERNEL); 301 if (!ptr2) { 302 pr_err("Allocation failed\n"); 303 return; 304 } 305 306 ptr1[40] = 'x'; 307 if (ptr1 == ptr2) 308 pr_err("Could not detect use-after-free: ptr1 == ptr2\n"); 309 kfree(ptr2); 310} 311 312static noinline void __init kmem_cache_oob(void) 313{ 314 char *p; 315 size_t size = 200; 316 struct kmem_cache *cache = kmem_cache_create("test_cache", 317 size, 0, 318 0, NULL); 319 if (!cache) { 320 pr_err("Cache allocation failed\n"); 321 return; 322 } 323 pr_info("out-of-bounds in kmem_cache_alloc\n"); 324 p = kmem_cache_alloc(cache, GFP_KERNEL); 325 if (!p) { 326 pr_err("Allocation failed\n"); 327 kmem_cache_destroy(cache); 328 return; 329 } 330 331 *p = p[size]; 332 kmem_cache_free(cache, p); 333 kmem_cache_destroy(cache); 334} 335 336static noinline void __init memcg_accounted_kmem_cache(void) 337{ 338 int i; 339 char *p; 340 size_t size = 200; 341 struct kmem_cache *cache; 342 343 cache = kmem_cache_create("test_cache", size, 0, SLAB_ACCOUNT, NULL); 344 if (!cache) { 345 pr_err("Cache allocation failed\n"); 346 return; 347 } 348 349 pr_info("allocate memcg accounted object\n"); 350 /* 351 * Several allocations with a delay to allow for lazy per memcg kmem 352 * cache creation. 353 */ 354 for (i = 0; i < 5; i++) { 355 p = kmem_cache_alloc(cache, GFP_KERNEL); 356 if (!p) { 357 pr_err("Allocation failed\n"); 358 goto free_cache; 359 } 360 kmem_cache_free(cache, p); 361 msleep(100); 362 } 363 364free_cache: 365 kmem_cache_destroy(cache); 366} 367 368static char global_array[10]; 369 370static noinline void __init kasan_global_oob(void) 371{ 372 volatile int i = 3; 373 char *p = &global_array[ARRAY_SIZE(global_array) + i]; 374 375 pr_info("out-of-bounds global variable\n"); 376 *(volatile char *)p; 377} 378 379static noinline void __init kasan_stack_oob(void) 380{ 381 char stack_array[10]; 382 volatile int i = 0; 383 char *p = &stack_array[ARRAY_SIZE(stack_array) + i]; 384 385 pr_info("out-of-bounds on stack\n"); 386 *(volatile char *)p; 387} 388 389static noinline void __init ksize_unpoisons_memory(void) 390{ 391 char *ptr; 392 size_t size = 123, real_size = size; 393 394 pr_info("ksize() unpoisons the whole allocated chunk\n"); 395 ptr = kmalloc(size, GFP_KERNEL); 396 if (!ptr) { 397 pr_err("Allocation failed\n"); 398 return; 399 } 400 real_size = ksize(ptr); 401 /* This access doesn't trigger an error. */ 402 ptr[size] = 'x'; 403 /* This one does. */ 404 ptr[real_size] = 'y'; 405 kfree(ptr); 406} 407 408static noinline void __init copy_user_test(void) 409{ 410 char *kmem; 411 char __user *usermem; 412 size_t size = 10; 413 int unused; 414 415 kmem = kmalloc(size, GFP_KERNEL); 416 if (!kmem) 417 return; 418 419 usermem = (char __user *)vm_mmap(NULL, 0, PAGE_SIZE, 420 PROT_READ | PROT_WRITE | PROT_EXEC, 421 MAP_ANONYMOUS | MAP_PRIVATE, 0); 422 if (IS_ERR(usermem)) { 423 pr_err("Failed to allocate user memory\n"); 424 kfree(kmem); 425 return; 426 } 427 428 pr_info("out-of-bounds in copy_from_user()\n"); 429 unused = copy_from_user(kmem, usermem, size + 1); 430 431 pr_info("out-of-bounds in copy_to_user()\n"); 432 unused = copy_to_user(usermem, kmem, size + 1); 433 434 pr_info("out-of-bounds in __copy_from_user()\n"); 435 unused = __copy_from_user(kmem, usermem, size + 1); 436 437 pr_info("out-of-bounds in __copy_to_user()\n"); 438 unused = __copy_to_user(usermem, kmem, size + 1); 439 440 pr_info("out-of-bounds in __copy_from_user_inatomic()\n"); 441 unused = __copy_from_user_inatomic(kmem, usermem, size + 1); 442 443 pr_info("out-of-bounds in __copy_to_user_inatomic()\n"); 444 unused = __copy_to_user_inatomic(usermem, kmem, size + 1); 445 446 pr_info("out-of-bounds in strncpy_from_user()\n"); 447 unused = strncpy_from_user(kmem, usermem, size + 1); 448 449 vm_munmap((unsigned long)usermem, PAGE_SIZE); 450 kfree(kmem); 451} 452 453static noinline void __init use_after_scope_test(void) 454{ 455 volatile char *volatile p; 456 457 pr_info("use-after-scope on int\n"); 458 { 459 int local = 0; 460 461 p = (char *)&local; 462 } 463 p[0] = 1; 464 p[3] = 1; 465 466 pr_info("use-after-scope on array\n"); 467 { 468 char local[1024] = {0}; 469 470 p = local; 471 } 472 p[0] = 1; 473 p[1023] = 1; 474} 475 476static int __init kmalloc_tests_init(void) 477{ 478 /* 479 * Temporarily enable multi-shot mode. Otherwise, we'd only get a 480 * report for the first case. 481 */ 482 bool multishot = kasan_save_enable_multi_shot(); 483 484 kmalloc_oob_right(); 485 kmalloc_oob_left(); 486 kmalloc_node_oob_right(); 487#ifdef CONFIG_SLUB 488 kmalloc_pagealloc_oob_right(); 489#endif 490 kmalloc_large_oob_right(); 491 kmalloc_oob_krealloc_more(); 492 kmalloc_oob_krealloc_less(); 493 kmalloc_oob_16(); 494 kmalloc_oob_in_memset(); 495 kmalloc_oob_memset_2(); 496 kmalloc_oob_memset_4(); 497 kmalloc_oob_memset_8(); 498 kmalloc_oob_memset_16(); 499 kmalloc_uaf(); 500 kmalloc_uaf_memset(); 501 kmalloc_uaf2(); 502 kmem_cache_oob(); 503 memcg_accounted_kmem_cache(); 504 kasan_stack_oob(); 505 kasan_global_oob(); 506 ksize_unpoisons_memory(); 507 copy_user_test(); 508 use_after_scope_test(); 509 510 kasan_restore_multi_shot(multishot); 511 512 return -EAGAIN; 513} 514 515module_init(kmalloc_tests_init); 516MODULE_LICENSE("GPL");