|
4 | 4 | * page allocation and slab allocations. |
5 | 5 | */ |
6 | 6 | #include "lkdtm.h" |
| 7 | +#include <linux/kfence.h> |
7 | 8 | #include <linux/slab.h> |
8 | 9 | #include <linux/vmalloc.h> |
9 | 10 | #include <linux/sched.h> |
@@ -132,6 +133,64 @@ static void lkdtm_READ_AFTER_FREE(void) |
132 | 133 | kfree(val); |
133 | 134 | } |
134 | 135 |
|
| 136 | +static 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 or |
| 143 | + * the middle of the allocation to store the free pointer, |
| 144 | + * depending on configurations. Store in the second word to |
| 145 | + * 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 get |
| 151 | + * 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-gate |
| 156 | + * timer can catch up: after @resched_after, every failed allocation |
| 157 | + * 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 | + |
135 | 194 | static void lkdtm_WRITE_BUDDY_AFTER_FREE(void) |
136 | 195 | { |
137 | 196 | unsigned long p = __get_free_page(GFP_KERNEL); |
@@ -327,6 +386,7 @@ static struct crashtype crashtypes[] = { |
327 | 386 | CRASHTYPE(VMALLOC_LINEAR_OVERFLOW), |
328 | 387 | CRASHTYPE(WRITE_AFTER_FREE), |
329 | 388 | CRASHTYPE(READ_AFTER_FREE), |
| 389 | + CRASHTYPE(KFENCE_READ_AFTER_FREE), |
330 | 390 | CRASHTYPE(WRITE_BUDDY_AFTER_FREE), |
331 | 391 | CRASHTYPE(READ_BUDDY_AFTER_FREE), |
332 | 392 | CRASHTYPE(SLAB_INIT_ON_ALLOC), |
|
0 commit comments