403 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * This is for all the tests relating directly to heap memory, including4 * page allocation and slab allocations.5 */6#include "lkdtm.h"7#include <linux/kfence.h>8#include <linux/slab.h>9#include <linux/vmalloc.h>10#include <linux/sched.h>11 12static struct kmem_cache *double_free_cache;13static struct kmem_cache *a_cache;14static struct kmem_cache *b_cache;15 16/*17 * Using volatile here means the compiler cannot ever make assumptions18 * about this value. This means compile-time length checks involving19 * this variable cannot be performed; only run-time checks.20 */21static volatile int __offset = 1;22 23/*24 * If there aren't guard pages, it's likely that a consecutive allocation will25 * let us overflow into the second allocation without overwriting something real.26 *27 * This should always be caught because there is an unconditional unmapped28 * page after vmap allocations.29 */30static void lkdtm_VMALLOC_LINEAR_OVERFLOW(void)31{32 char *one, *two;33 34 one = vzalloc(PAGE_SIZE);35 OPTIMIZER_HIDE_VAR(one);36 two = vzalloc(PAGE_SIZE);37 38 pr_info("Attempting vmalloc linear overflow ...\n");39 memset(one, 0xAA, PAGE_SIZE + __offset);40 41 vfree(two);42 vfree(one);43}44 45/*46 * This tries to stay within the next largest power-of-2 kmalloc cache47 * to avoid actually overwriting anything important if it's not detected48 * correctly.49 *50 * This should get caught by either memory tagging, KASan, or by using51 * CONFIG_SLUB_DEBUG=y and slab_debug=ZF (or CONFIG_SLUB_DEBUG_ON=y).52 */53static void lkdtm_SLAB_LINEAR_OVERFLOW(void)54{55 size_t len = 1020;56 u32 *data = kmalloc(len, GFP_KERNEL);57 if (!data)58 return;59 60 pr_info("Attempting slab linear overflow ...\n");61 OPTIMIZER_HIDE_VAR(data);62 data[1024 / sizeof(u32)] = 0x12345678;63 kfree(data);64}65 66static void lkdtm_WRITE_AFTER_FREE(void)67{68 int *base, *again;69 size_t len = 1024;70 /*71 * The slub allocator uses the first word to store the free72 * pointer in some configurations. Use the middle of the73 * allocation to avoid running into the freelist74 */75 size_t offset = (len / sizeof(*base)) / 2;76 77 base = kmalloc(len, GFP_KERNEL);78 if (!base)79 return;80 pr_info("Allocated memory %p-%p\n", base, &base[offset * 2]);81 pr_info("Attempting bad write to freed memory at %p\n",82 &base[offset]);83 kfree(base);84 base[offset] = 0x0abcdef0;85 /* Attempt to notice the overwrite. */86 again = kmalloc(len, GFP_KERNEL);87 kfree(again);88 if (again != base)89 pr_info("Hmm, didn't get the same memory range.\n");90}91 92static void lkdtm_READ_AFTER_FREE(void)93{94 int *base, *val, saw;95 size_t len = 1024;96 /*97 * The slub allocator will use the either the first word or98 * the middle of the allocation to store the free pointer,99 * depending on configurations. Store in the second word to100 * avoid running into the freelist.101 */102 size_t offset = sizeof(*base);103 104 base = kmalloc(len, GFP_KERNEL);105 if (!base) {106 pr_info("Unable to allocate base memory.\n");107 return;108 }109 110 val = kmalloc(len, GFP_KERNEL);111 if (!val) {112 pr_info("Unable to allocate val memory.\n");113 kfree(base);114 return;115 }116 117 *val = 0x12345678;118 base[offset] = *val;119 pr_info("Value in memory before free: %x\n", base[offset]);120 121 kfree(base);122 123 pr_info("Attempting bad read from freed memory\n");124 saw = base[offset];125 if (saw != *val) {126 /* Good! Poisoning happened, so declare a win. */127 pr_info("Memory correctly poisoned (%x)\n", saw);128 } else {129 pr_err("FAIL: Memory was not poisoned!\n");130 pr_expected_config_param(CONFIG_INIT_ON_FREE_DEFAULT_ON, "init_on_free");131 }132 133 kfree(val);134}135 136static void lkdtm_KFENCE_READ_AFTER_FREE(void)137{138 int *base, val, saw;139 unsigned long timeout, resched_after;140 size_t len = 1024;141 /*142 * The slub allocator will use the either the first word or143 * the middle of the allocation to store the free pointer,144 * depending on configurations. Store in the second word to145 * avoid running into the freelist.146 */147 size_t offset = sizeof(*base);148 149 /*150 * 100x the sample interval should be more than enough to ensure we get151 * a KFENCE allocation eventually.152 */153 timeout = jiffies + msecs_to_jiffies(100 * kfence_sample_interval);154 /*155 * Especially for non-preemption kernels, ensure the allocation-gate156 * timer can catch up: after @resched_after, every failed allocation157 * attempt yields, to ensure the allocation-gate timer is scheduled.158 */159 resched_after = jiffies + msecs_to_jiffies(kfence_sample_interval);160 do {161 base = kmalloc(len, GFP_KERNEL);162 if (!base) {163 pr_err("FAIL: Unable to allocate kfence memory!\n");164 return;165 }166 167 if (is_kfence_address(base)) {168 val = 0x12345678;169 base[offset] = val;170 pr_info("Value in memory before free: %x\n", base[offset]);171 172 kfree(base);173 174 pr_info("Attempting bad read from freed memory\n");175 saw = base[offset];176 if (saw != val) {177 /* Good! Poisoning happened, so declare a win. */178 pr_info("Memory correctly poisoned (%x)\n", saw);179 } else {180 pr_err("FAIL: Memory was not poisoned!\n");181 pr_expected_config_param(CONFIG_INIT_ON_FREE_DEFAULT_ON, "init_on_free");182 }183 return;184 }185 186 kfree(base);187 if (time_after(jiffies, resched_after))188 cond_resched();189 } while (time_before(jiffies, timeout));190 191 pr_err("FAIL: kfence memory never allocated!\n");192}193 194static void lkdtm_WRITE_BUDDY_AFTER_FREE(void)195{196 unsigned long p = __get_free_page(GFP_KERNEL);197 if (!p) {198 pr_info("Unable to allocate free page\n");199 return;200 }201 202 pr_info("Writing to the buddy page before free\n");203 memset((void *)p, 0x3, PAGE_SIZE);204 free_page(p);205 schedule();206 pr_info("Attempting bad write to the buddy page after free\n");207 memset((void *)p, 0x78, PAGE_SIZE);208 /* Attempt to notice the overwrite. */209 p = __get_free_page(GFP_KERNEL);210 free_page(p);211 schedule();212}213 214static void lkdtm_READ_BUDDY_AFTER_FREE(void)215{216 unsigned long p = __get_free_page(GFP_KERNEL);217 int saw, *val;218 int *base;219 220 if (!p) {221 pr_info("Unable to allocate free page\n");222 return;223 }224 225 val = kmalloc(1024, GFP_KERNEL);226 if (!val) {227 pr_info("Unable to allocate val memory.\n");228 free_page(p);229 return;230 }231 232 base = (int *)p;233 234 *val = 0x12345678;235 base[0] = *val;236 pr_info("Value in memory before free: %x\n", base[0]);237 free_page(p);238 pr_info("Attempting to read from freed memory\n");239 saw = base[0];240 if (saw != *val) {241 /* Good! Poisoning happened, so declare a win. */242 pr_info("Memory correctly poisoned (%x)\n", saw);243 } else {244 pr_err("FAIL: Buddy page was not poisoned!\n");245 pr_expected_config_param(CONFIG_INIT_ON_FREE_DEFAULT_ON, "init_on_free");246 }247 248 kfree(val);249}250 251static void lkdtm_SLAB_INIT_ON_ALLOC(void)252{253 u8 *first;254 u8 *val;255 256 first = kmalloc(512, GFP_KERNEL);257 if (!first) {258 pr_info("Unable to allocate 512 bytes the first time.\n");259 return;260 }261 262 memset(first, 0xAB, 512);263 kfree(first);264 265 val = kmalloc(512, GFP_KERNEL);266 if (!val) {267 pr_info("Unable to allocate 512 bytes the second time.\n");268 return;269 }270 if (val != first) {271 pr_warn("Reallocation missed clobbered memory.\n");272 }273 274 if (memchr(val, 0xAB, 512) == NULL) {275 pr_info("Memory appears initialized (%x, no earlier values)\n", *val);276 } else {277 pr_err("FAIL: Slab was not initialized\n");278 pr_expected_config_param(CONFIG_INIT_ON_ALLOC_DEFAULT_ON, "init_on_alloc");279 }280 kfree(val);281}282 283static void lkdtm_BUDDY_INIT_ON_ALLOC(void)284{285 u8 *first;286 u8 *val;287 288 first = (u8 *)__get_free_page(GFP_KERNEL);289 if (!first) {290 pr_info("Unable to allocate first free page\n");291 return;292 }293 294 memset(first, 0xAB, PAGE_SIZE);295 free_page((unsigned long)first);296 297 val = (u8 *)__get_free_page(GFP_KERNEL);298 if (!val) {299 pr_info("Unable to allocate second free page\n");300 return;301 }302 303 if (val != first) {304 pr_warn("Reallocation missed clobbered memory.\n");305 }306 307 if (memchr(val, 0xAB, PAGE_SIZE) == NULL) {308 pr_info("Memory appears initialized (%x, no earlier values)\n", *val);309 } else {310 pr_err("FAIL: Slab was not initialized\n");311 pr_expected_config_param(CONFIG_INIT_ON_ALLOC_DEFAULT_ON, "init_on_alloc");312 }313 free_page((unsigned long)val);314}315 316static void lkdtm_SLAB_FREE_DOUBLE(void)317{318 int *val;319 320 val = kmem_cache_alloc(double_free_cache, GFP_KERNEL);321 if (!val) {322 pr_info("Unable to allocate double_free_cache memory.\n");323 return;324 }325 326 /* Just make sure we got real memory. */327 *val = 0x12345678;328 pr_info("Attempting double slab free ...\n");329 kmem_cache_free(double_free_cache, val);330 kmem_cache_free(double_free_cache, val);331}332 333static void lkdtm_SLAB_FREE_CROSS(void)334{335 int *val;336 337 val = kmem_cache_alloc(a_cache, GFP_KERNEL);338 if (!val) {339 pr_info("Unable to allocate a_cache memory.\n");340 return;341 }342 343 /* Just make sure we got real memory. */344 *val = 0x12345679;345 pr_info("Attempting cross-cache slab free ...\n");346 kmem_cache_free(b_cache, val);347}348 349static void lkdtm_SLAB_FREE_PAGE(void)350{351 unsigned long p = __get_free_page(GFP_KERNEL);352 353 pr_info("Attempting non-Slab slab free ...\n");354 kmem_cache_free(NULL, (void *)p);355 free_page(p);356}357 358/*359 * We have constructors to keep the caches distinctly separated without360 * needing to boot with "slab_nomerge".361 */362static void ctor_double_free(void *region)363{ }364static void ctor_a(void *region)365{ }366static void ctor_b(void *region)367{ }368 369void __init lkdtm_heap_init(void)370{371 double_free_cache = kmem_cache_create("lkdtm-heap-double_free",372 64, 0, 0, ctor_double_free);373 a_cache = kmem_cache_create("lkdtm-heap-a", 64, 0, 0, ctor_a);374 b_cache = kmem_cache_create("lkdtm-heap-b", 64, 0, 0, ctor_b);375}376 377void __exit lkdtm_heap_exit(void)378{379 kmem_cache_destroy(double_free_cache);380 kmem_cache_destroy(a_cache);381 kmem_cache_destroy(b_cache);382}383 384static struct crashtype crashtypes[] = {385 CRASHTYPE(SLAB_LINEAR_OVERFLOW),386 CRASHTYPE(VMALLOC_LINEAR_OVERFLOW),387 CRASHTYPE(WRITE_AFTER_FREE),388 CRASHTYPE(READ_AFTER_FREE),389 CRASHTYPE(KFENCE_READ_AFTER_FREE),390 CRASHTYPE(WRITE_BUDDY_AFTER_FREE),391 CRASHTYPE(READ_BUDDY_AFTER_FREE),392 CRASHTYPE(SLAB_INIT_ON_ALLOC),393 CRASHTYPE(BUDDY_INIT_ON_ALLOC),394 CRASHTYPE(SLAB_FREE_DOUBLE),395 CRASHTYPE(SLAB_FREE_CROSS),396 CRASHTYPE(SLAB_FREE_PAGE),397};398 399struct crashtype_category heap_crashtypes = {400 .crashtypes = crashtypes,401 .len = ARRAY_SIZE(crashtypes),402};403