xref: /openbmc/linux/drivers/misc/lkdtm/heap.c (revision 966fede8)
1039a1c42SKees Cook // SPDX-License-Identifier: GPL-2.0
2039a1c42SKees Cook /*
3039a1c42SKees Cook  * This is for all the tests relating directly to heap memory, including
4039a1c42SKees Cook  * page allocation and slab allocations.
5039a1c42SKees Cook  */
6039a1c42SKees Cook #include "lkdtm.h"
7039a1c42SKees Cook #include <linux/slab.h>
8039a1c42SKees Cook #include <linux/sched.h>
9039a1c42SKees Cook 
10966fede8SKees Cook static struct kmem_cache *double_free_cache;
11966fede8SKees Cook static struct kmem_cache *a_cache;
12966fede8SKees Cook static struct kmem_cache *b_cache;
13966fede8SKees Cook 
14039a1c42SKees Cook /*
15039a1c42SKees Cook  * This tries to stay within the next largest power-of-2 kmalloc cache
16039a1c42SKees Cook  * to avoid actually overwriting anything important if it's not detected
17039a1c42SKees Cook  * correctly.
18039a1c42SKees Cook  */
19039a1c42SKees Cook void lkdtm_OVERWRITE_ALLOCATION(void)
20039a1c42SKees Cook {
21039a1c42SKees Cook 	size_t len = 1020;
22039a1c42SKees Cook 	u32 *data = kmalloc(len, GFP_KERNEL);
23039a1c42SKees Cook 	if (!data)
24039a1c42SKees Cook 		return;
25039a1c42SKees Cook 
26039a1c42SKees Cook 	data[1024 / sizeof(u32)] = 0x12345678;
27039a1c42SKees Cook 	kfree(data);
28039a1c42SKees Cook }
29039a1c42SKees Cook 
30039a1c42SKees Cook void lkdtm_WRITE_AFTER_FREE(void)
31039a1c42SKees Cook {
32039a1c42SKees Cook 	int *base, *again;
33039a1c42SKees Cook 	size_t len = 1024;
34039a1c42SKees Cook 	/*
35039a1c42SKees Cook 	 * The slub allocator uses the first word to store the free
36039a1c42SKees Cook 	 * pointer in some configurations. Use the middle of the
37039a1c42SKees Cook 	 * allocation to avoid running into the freelist
38039a1c42SKees Cook 	 */
39039a1c42SKees Cook 	size_t offset = (len / sizeof(*base)) / 2;
40039a1c42SKees Cook 
41039a1c42SKees Cook 	base = kmalloc(len, GFP_KERNEL);
42039a1c42SKees Cook 	if (!base)
43039a1c42SKees Cook 		return;
44039a1c42SKees Cook 	pr_info("Allocated memory %p-%p\n", base, &base[offset * 2]);
45039a1c42SKees Cook 	pr_info("Attempting bad write to freed memory at %p\n",
46039a1c42SKees Cook 		&base[offset]);
47039a1c42SKees Cook 	kfree(base);
48039a1c42SKees Cook 	base[offset] = 0x0abcdef0;
49039a1c42SKees Cook 	/* Attempt to notice the overwrite. */
50039a1c42SKees Cook 	again = kmalloc(len, GFP_KERNEL);
51039a1c42SKees Cook 	kfree(again);
52039a1c42SKees Cook 	if (again != base)
53039a1c42SKees Cook 		pr_info("Hmm, didn't get the same memory range.\n");
54039a1c42SKees Cook }
55039a1c42SKees Cook 
56039a1c42SKees Cook void lkdtm_READ_AFTER_FREE(void)
57039a1c42SKees Cook {
58039a1c42SKees Cook 	int *base, *val, saw;
59039a1c42SKees Cook 	size_t len = 1024;
60039a1c42SKees Cook 	/*
61039a1c42SKees Cook 	 * The slub allocator uses the first word to store the free
62039a1c42SKees Cook 	 * pointer in some configurations. Use the middle of the
63039a1c42SKees Cook 	 * allocation to avoid running into the freelist
64039a1c42SKees Cook 	 */
65039a1c42SKees Cook 	size_t offset = (len / sizeof(*base)) / 2;
66039a1c42SKees Cook 
67039a1c42SKees Cook 	base = kmalloc(len, GFP_KERNEL);
68039a1c42SKees Cook 	if (!base) {
69039a1c42SKees Cook 		pr_info("Unable to allocate base memory.\n");
70039a1c42SKees Cook 		return;
71039a1c42SKees Cook 	}
72039a1c42SKees Cook 
73039a1c42SKees Cook 	val = kmalloc(len, GFP_KERNEL);
74039a1c42SKees Cook 	if (!val) {
75039a1c42SKees Cook 		pr_info("Unable to allocate val memory.\n");
76039a1c42SKees Cook 		kfree(base);
77039a1c42SKees Cook 		return;
78039a1c42SKees Cook 	}
79039a1c42SKees Cook 
80039a1c42SKees Cook 	*val = 0x12345678;
81039a1c42SKees Cook 	base[offset] = *val;
82039a1c42SKees Cook 	pr_info("Value in memory before free: %x\n", base[offset]);
83039a1c42SKees Cook 
84039a1c42SKees Cook 	kfree(base);
85039a1c42SKees Cook 
86039a1c42SKees Cook 	pr_info("Attempting bad read from freed memory\n");
87039a1c42SKees Cook 	saw = base[offset];
88039a1c42SKees Cook 	if (saw != *val) {
89039a1c42SKees Cook 		/* Good! Poisoning happened, so declare a win. */
90039a1c42SKees Cook 		pr_info("Memory correctly poisoned (%x)\n", saw);
91039a1c42SKees Cook 		BUG();
92039a1c42SKees Cook 	}
93039a1c42SKees Cook 	pr_info("Memory was not poisoned\n");
94039a1c42SKees Cook 
95039a1c42SKees Cook 	kfree(val);
96039a1c42SKees Cook }
97039a1c42SKees Cook 
98039a1c42SKees Cook void lkdtm_WRITE_BUDDY_AFTER_FREE(void)
99039a1c42SKees Cook {
100039a1c42SKees Cook 	unsigned long p = __get_free_page(GFP_KERNEL);
101039a1c42SKees Cook 	if (!p) {
102039a1c42SKees Cook 		pr_info("Unable to allocate free page\n");
103039a1c42SKees Cook 		return;
104039a1c42SKees Cook 	}
105039a1c42SKees Cook 
106039a1c42SKees Cook 	pr_info("Writing to the buddy page before free\n");
107039a1c42SKees Cook 	memset((void *)p, 0x3, PAGE_SIZE);
108039a1c42SKees Cook 	free_page(p);
109039a1c42SKees Cook 	schedule();
110039a1c42SKees Cook 	pr_info("Attempting bad write to the buddy page after free\n");
111039a1c42SKees Cook 	memset((void *)p, 0x78, PAGE_SIZE);
112039a1c42SKees Cook 	/* Attempt to notice the overwrite. */
113039a1c42SKees Cook 	p = __get_free_page(GFP_KERNEL);
114039a1c42SKees Cook 	free_page(p);
115039a1c42SKees Cook 	schedule();
116039a1c42SKees Cook }
117039a1c42SKees Cook 
118039a1c42SKees Cook void lkdtm_READ_BUDDY_AFTER_FREE(void)
119039a1c42SKees Cook {
120039a1c42SKees Cook 	unsigned long p = __get_free_page(GFP_KERNEL);
121039a1c42SKees Cook 	int saw, *val;
122039a1c42SKees Cook 	int *base;
123039a1c42SKees Cook 
124039a1c42SKees Cook 	if (!p) {
125039a1c42SKees Cook 		pr_info("Unable to allocate free page\n");
126039a1c42SKees Cook 		return;
127039a1c42SKees Cook 	}
128039a1c42SKees Cook 
129039a1c42SKees Cook 	val = kmalloc(1024, GFP_KERNEL);
130039a1c42SKees Cook 	if (!val) {
131039a1c42SKees Cook 		pr_info("Unable to allocate val memory.\n");
132039a1c42SKees Cook 		free_page(p);
133039a1c42SKees Cook 		return;
134039a1c42SKees Cook 	}
135039a1c42SKees Cook 
136039a1c42SKees Cook 	base = (int *)p;
137039a1c42SKees Cook 
138039a1c42SKees Cook 	*val = 0x12345678;
139039a1c42SKees Cook 	base[0] = *val;
140039a1c42SKees Cook 	pr_info("Value in memory before free: %x\n", base[0]);
141039a1c42SKees Cook 	free_page(p);
142039a1c42SKees Cook 	pr_info("Attempting to read from freed memory\n");
143039a1c42SKees Cook 	saw = base[0];
144039a1c42SKees Cook 	if (saw != *val) {
145039a1c42SKees Cook 		/* Good! Poisoning happened, so declare a win. */
146039a1c42SKees Cook 		pr_info("Memory correctly poisoned (%x)\n", saw);
147039a1c42SKees Cook 		BUG();
148039a1c42SKees Cook 	}
149039a1c42SKees Cook 	pr_info("Buddy page was not poisoned\n");
150039a1c42SKees Cook 
151039a1c42SKees Cook 	kfree(val);
152039a1c42SKees Cook }
153966fede8SKees Cook 
154966fede8SKees Cook void lkdtm_SLAB_FREE_DOUBLE(void)
155966fede8SKees Cook {
156966fede8SKees Cook 	int *val;
157966fede8SKees Cook 
158966fede8SKees Cook 	val = kmem_cache_alloc(double_free_cache, GFP_KERNEL);
159966fede8SKees Cook 	if (!val) {
160966fede8SKees Cook 		pr_info("Unable to allocate double_free_cache memory.\n");
161966fede8SKees Cook 		return;
162966fede8SKees Cook 	}
163966fede8SKees Cook 
164966fede8SKees Cook 	/* Just make sure we got real memory. */
165966fede8SKees Cook 	*val = 0x12345678;
166966fede8SKees Cook 	pr_info("Attempting double slab free ...\n");
167966fede8SKees Cook 	kmem_cache_free(double_free_cache, val);
168966fede8SKees Cook 	kmem_cache_free(double_free_cache, val);
169966fede8SKees Cook }
170966fede8SKees Cook 
171966fede8SKees Cook void lkdtm_SLAB_FREE_CROSS(void)
172966fede8SKees Cook {
173966fede8SKees Cook 	int *val;
174966fede8SKees Cook 
175966fede8SKees Cook 	val = kmem_cache_alloc(a_cache, GFP_KERNEL);
176966fede8SKees Cook 	if (!val) {
177966fede8SKees Cook 		pr_info("Unable to allocate a_cache memory.\n");
178966fede8SKees Cook 		return;
179966fede8SKees Cook 	}
180966fede8SKees Cook 
181966fede8SKees Cook 	/* Just make sure we got real memory. */
182966fede8SKees Cook 	*val = 0x12345679;
183966fede8SKees Cook 	pr_info("Attempting cross-cache slab free ...\n");
184966fede8SKees Cook 	kmem_cache_free(b_cache, val);
185966fede8SKees Cook }
186966fede8SKees Cook 
187966fede8SKees Cook void lkdtm_SLAB_FREE_PAGE(void)
188966fede8SKees Cook {
189966fede8SKees Cook 	unsigned long p = __get_free_page(GFP_KERNEL);
190966fede8SKees Cook 
191966fede8SKees Cook 	pr_info("Attempting non-Slab slab free ...\n");
192966fede8SKees Cook 	kmem_cache_free(NULL, (void *)p);
193966fede8SKees Cook 	free_page(p);
194966fede8SKees Cook }
195966fede8SKees Cook 
196966fede8SKees Cook /*
197966fede8SKees Cook  * We have constructors to keep the caches distinctly separated without
198966fede8SKees Cook  * needing to boot with "slab_nomerge".
199966fede8SKees Cook  */
200966fede8SKees Cook static void ctor_double_free(void *region)
201966fede8SKees Cook { }
202966fede8SKees Cook static void ctor_a(void *region)
203966fede8SKees Cook { }
204966fede8SKees Cook static void ctor_b(void *region)
205966fede8SKees Cook { }
206966fede8SKees Cook 
207966fede8SKees Cook void __init lkdtm_heap_init(void)
208966fede8SKees Cook {
209966fede8SKees Cook 	double_free_cache = kmem_cache_create("lkdtm-heap-double_free",
210966fede8SKees Cook 					      64, 0, 0, ctor_double_free);
211966fede8SKees Cook 	a_cache = kmem_cache_create("lkdtm-heap-a", 64, 0, 0, ctor_a);
212966fede8SKees Cook 	b_cache = kmem_cache_create("lkdtm-heap-b", 64, 0, 0, ctor_b);
213966fede8SKees Cook }
214966fede8SKees Cook 
215966fede8SKees Cook void __exit lkdtm_heap_exit(void)
216966fede8SKees Cook {
217966fede8SKees Cook 	kmem_cache_destroy(double_free_cache);
218966fede8SKees Cook 	kmem_cache_destroy(a_cache);
219966fede8SKees Cook 	kmem_cache_destroy(b_cache);
220966fede8SKees Cook }
221