Skip to content

Commit aabf7c3

Browse files
bebarinokees
authored andcommitted
lkdtm: Add kfence read after free crash type
Add the ability to allocate memory from kfence and trigger a read after free on that memory to validate that kfence is working properly. This is used by ChromeOS integration tests to validate that kfence errors can be collected on user devices and parsed properly. Cc: Alexander Potapenko <glider@google.com> Acked-by: Marco Elver <elver@google.com> Cc: Dmitry Vyukov <dvyukov@google.com> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: kasan-dev@googlegroups.com Signed-off-by: Stephen Boyd <swboyd@chromium.org> Link: https://lore.kernel.org/r/20231129214413.3156334-1-swboyd@chromium.org Signed-off-by: Kees Cook <keescook@chromium.org>
1 parent e5a4975 commit aabf7c3

2 files changed

Lines changed: 62 additions & 0 deletions

File tree

drivers/misc/lkdtm/heap.c

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
* page allocation and slab allocations.
55
*/
66
#include "lkdtm.h"
7+
#include <linux/kfence.h>
78
#include <linux/slab.h>
89
#include <linux/vmalloc.h>
910
#include <linux/sched.h>
@@ -132,6 +133,64 @@ static void lkdtm_READ_AFTER_FREE(void)
132133
kfree(val);
133134
}
134135

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+
135194
static void lkdtm_WRITE_BUDDY_AFTER_FREE(void)
136195
{
137196
unsigned long p = __get_free_page(GFP_KERNEL);
@@ -327,6 +386,7 @@ static struct crashtype crashtypes[] = {
327386
CRASHTYPE(VMALLOC_LINEAR_OVERFLOW),
328387
CRASHTYPE(WRITE_AFTER_FREE),
329388
CRASHTYPE(READ_AFTER_FREE),
389+
CRASHTYPE(KFENCE_READ_AFTER_FREE),
330390
CRASHTYPE(WRITE_BUDDY_AFTER_FREE),
331391
CRASHTYPE(READ_BUDDY_AFTER_FREE),
332392
CRASHTYPE(SLAB_INIT_ON_ALLOC),

include/linux/kfence.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,8 @@ bool __kfence_obj_info(struct kmem_obj_info *kpp, void *object, struct slab *sla
223223

224224
#else /* CONFIG_KFENCE */
225225

226+
#define kfence_sample_interval (0)
227+
226228
static inline bool is_kfence_address(const void *addr) { return false; }
227229
static inline void kfence_alloc_pool_and_metadata(void) { }
228230
static inline void kfence_init(void) { }

0 commit comments

Comments
 (0)