xref: /openbmc/linux/drivers/misc/lkdtm/heap.c (revision e12145cf)
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 	/*
61*e12145cfSKees Cook 	 * The slub allocator will use the either the first word or
62*e12145cfSKees Cook 	 * the middle of the allocation to store the free pointer,
63*e12145cfSKees Cook 	 * depending on configurations. Store in the second word to
64*e12145cfSKees Cook 	 * avoid running into the freelist.
65039a1c42SKees Cook 	 */
66*e12145cfSKees Cook 	size_t offset = sizeof(*base);
67039a1c42SKees Cook 
68039a1c42SKees Cook 	base = kmalloc(len, GFP_KERNEL);
69039a1c42SKees Cook 	if (!base) {
70039a1c42SKees Cook 		pr_info("Unable to allocate base memory.\n");
71039a1c42SKees Cook 		return;
72039a1c42SKees Cook 	}
73039a1c42SKees Cook 
74039a1c42SKees Cook 	val = kmalloc(len, GFP_KERNEL);
75039a1c42SKees Cook 	if (!val) {
76039a1c42SKees Cook 		pr_info("Unable to allocate val memory.\n");
77039a1c42SKees Cook 		kfree(base);
78039a1c42SKees Cook 		return;
79039a1c42SKees Cook 	}
80039a1c42SKees Cook 
81039a1c42SKees Cook 	*val = 0x12345678;
82039a1c42SKees Cook 	base[offset] = *val;
83039a1c42SKees Cook 	pr_info("Value in memory before free: %x\n", base[offset]);
84039a1c42SKees Cook 
85039a1c42SKees Cook 	kfree(base);
86039a1c42SKees Cook 
87039a1c42SKees Cook 	pr_info("Attempting bad read from freed memory\n");
88039a1c42SKees Cook 	saw = base[offset];
89039a1c42SKees Cook 	if (saw != *val) {
90039a1c42SKees Cook 		/* Good! Poisoning happened, so declare a win. */
91039a1c42SKees Cook 		pr_info("Memory correctly poisoned (%x)\n", saw);
92039a1c42SKees Cook 		BUG();
93039a1c42SKees Cook 	}
94039a1c42SKees Cook 	pr_info("Memory was not poisoned\n");
95039a1c42SKees Cook 
96039a1c42SKees Cook 	kfree(val);
97039a1c42SKees Cook }
98039a1c42SKees Cook 
99039a1c42SKees Cook void lkdtm_WRITE_BUDDY_AFTER_FREE(void)
100039a1c42SKees Cook {
101039a1c42SKees Cook 	unsigned long p = __get_free_page(GFP_KERNEL);
102039a1c42SKees Cook 	if (!p) {
103039a1c42SKees Cook 		pr_info("Unable to allocate free page\n");
104039a1c42SKees Cook 		return;
105039a1c42SKees Cook 	}
106039a1c42SKees Cook 
107039a1c42SKees Cook 	pr_info("Writing to the buddy page before free\n");
108039a1c42SKees Cook 	memset((void *)p, 0x3, PAGE_SIZE);
109039a1c42SKees Cook 	free_page(p);
110039a1c42SKees Cook 	schedule();
111039a1c42SKees Cook 	pr_info("Attempting bad write to the buddy page after free\n");
112039a1c42SKees Cook 	memset((void *)p, 0x78, PAGE_SIZE);
113039a1c42SKees Cook 	/* Attempt to notice the overwrite. */
114039a1c42SKees Cook 	p = __get_free_page(GFP_KERNEL);
115039a1c42SKees Cook 	free_page(p);
116039a1c42SKees Cook 	schedule();
117039a1c42SKees Cook }
118039a1c42SKees Cook 
119039a1c42SKees Cook void lkdtm_READ_BUDDY_AFTER_FREE(void)
120039a1c42SKees Cook {
121039a1c42SKees Cook 	unsigned long p = __get_free_page(GFP_KERNEL);
122039a1c42SKees Cook 	int saw, *val;
123039a1c42SKees Cook 	int *base;
124039a1c42SKees Cook 
125039a1c42SKees Cook 	if (!p) {
126039a1c42SKees Cook 		pr_info("Unable to allocate free page\n");
127039a1c42SKees Cook 		return;
128039a1c42SKees Cook 	}
129039a1c42SKees Cook 
130039a1c42SKees Cook 	val = kmalloc(1024, GFP_KERNEL);
131039a1c42SKees Cook 	if (!val) {
132039a1c42SKees Cook 		pr_info("Unable to allocate val memory.\n");
133039a1c42SKees Cook 		free_page(p);
134039a1c42SKees Cook 		return;
135039a1c42SKees Cook 	}
136039a1c42SKees Cook 
137039a1c42SKees Cook 	base = (int *)p;
138039a1c42SKees Cook 
139039a1c42SKees Cook 	*val = 0x12345678;
140039a1c42SKees Cook 	base[0] = *val;
141039a1c42SKees Cook 	pr_info("Value in memory before free: %x\n", base[0]);
142039a1c42SKees Cook 	free_page(p);
143039a1c42SKees Cook 	pr_info("Attempting to read from freed memory\n");
144039a1c42SKees Cook 	saw = base[0];
145039a1c42SKees Cook 	if (saw != *val) {
146039a1c42SKees Cook 		/* Good! Poisoning happened, so declare a win. */
147039a1c42SKees Cook 		pr_info("Memory correctly poisoned (%x)\n", saw);
148039a1c42SKees Cook 		BUG();
149039a1c42SKees Cook 	}
150039a1c42SKees Cook 	pr_info("Buddy page was not poisoned\n");
151039a1c42SKees Cook 
152039a1c42SKees Cook 	kfree(val);
153039a1c42SKees Cook }
154966fede8SKees Cook 
155966fede8SKees Cook void lkdtm_SLAB_FREE_DOUBLE(void)
156966fede8SKees Cook {
157966fede8SKees Cook 	int *val;
158966fede8SKees Cook 
159966fede8SKees Cook 	val = kmem_cache_alloc(double_free_cache, GFP_KERNEL);
160966fede8SKees Cook 	if (!val) {
161966fede8SKees Cook 		pr_info("Unable to allocate double_free_cache memory.\n");
162966fede8SKees Cook 		return;
163966fede8SKees Cook 	}
164966fede8SKees Cook 
165966fede8SKees Cook 	/* Just make sure we got real memory. */
166966fede8SKees Cook 	*val = 0x12345678;
167966fede8SKees Cook 	pr_info("Attempting double slab free ...\n");
168966fede8SKees Cook 	kmem_cache_free(double_free_cache, val);
169966fede8SKees Cook 	kmem_cache_free(double_free_cache, val);
170966fede8SKees Cook }
171966fede8SKees Cook 
172966fede8SKees Cook void lkdtm_SLAB_FREE_CROSS(void)
173966fede8SKees Cook {
174966fede8SKees Cook 	int *val;
175966fede8SKees Cook 
176966fede8SKees Cook 	val = kmem_cache_alloc(a_cache, GFP_KERNEL);
177966fede8SKees Cook 	if (!val) {
178966fede8SKees Cook 		pr_info("Unable to allocate a_cache memory.\n");
179966fede8SKees Cook 		return;
180966fede8SKees Cook 	}
181966fede8SKees Cook 
182966fede8SKees Cook 	/* Just make sure we got real memory. */
183966fede8SKees Cook 	*val = 0x12345679;
184966fede8SKees Cook 	pr_info("Attempting cross-cache slab free ...\n");
185966fede8SKees Cook 	kmem_cache_free(b_cache, val);
186966fede8SKees Cook }
187966fede8SKees Cook 
188966fede8SKees Cook void lkdtm_SLAB_FREE_PAGE(void)
189966fede8SKees Cook {
190966fede8SKees Cook 	unsigned long p = __get_free_page(GFP_KERNEL);
191966fede8SKees Cook 
192966fede8SKees Cook 	pr_info("Attempting non-Slab slab free ...\n");
193966fede8SKees Cook 	kmem_cache_free(NULL, (void *)p);
194966fede8SKees Cook 	free_page(p);
195966fede8SKees Cook }
196966fede8SKees Cook 
197966fede8SKees Cook /*
198966fede8SKees Cook  * We have constructors to keep the caches distinctly separated without
199966fede8SKees Cook  * needing to boot with "slab_nomerge".
200966fede8SKees Cook  */
201966fede8SKees Cook static void ctor_double_free(void *region)
202966fede8SKees Cook { }
203966fede8SKees Cook static void ctor_a(void *region)
204966fede8SKees Cook { }
205966fede8SKees Cook static void ctor_b(void *region)
206966fede8SKees Cook { }
207966fede8SKees Cook 
208966fede8SKees Cook void __init lkdtm_heap_init(void)
209966fede8SKees Cook {
210966fede8SKees Cook 	double_free_cache = kmem_cache_create("lkdtm-heap-double_free",
211966fede8SKees Cook 					      64, 0, 0, ctor_double_free);
212966fede8SKees Cook 	a_cache = kmem_cache_create("lkdtm-heap-a", 64, 0, 0, ctor_a);
213966fede8SKees Cook 	b_cache = kmem_cache_create("lkdtm-heap-b", 64, 0, 0, ctor_b);
214966fede8SKees Cook }
215966fede8SKees Cook 
216966fede8SKees Cook void __exit lkdtm_heap_exit(void)
217966fede8SKees Cook {
218966fede8SKees Cook 	kmem_cache_destroy(double_free_cache);
219966fede8SKees Cook 	kmem_cache_destroy(a_cache);
220966fede8SKees Cook 	kmem_cache_destroy(b_cache);
221966fede8SKees Cook }
222