xref: /openbmc/linux/drivers/misc/lkdtm/heap.c (revision 5b777131)
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>
89c4f6ebcSKees Cook #include <linux/vmalloc.h>
9039a1c42SKees Cook #include <linux/sched.h>
10039a1c42SKees Cook 
11966fede8SKees Cook static struct kmem_cache *double_free_cache;
12966fede8SKees Cook static struct kmem_cache *a_cache;
13966fede8SKees Cook static struct kmem_cache *b_cache;
14966fede8SKees Cook 
15039a1c42SKees Cook /*
169c4f6ebcSKees Cook  * If there aren't guard pages, it's likely that a consecutive allocation will
179c4f6ebcSKees Cook  * let us overflow into the second allocation without overwriting something real.
189c4f6ebcSKees Cook  */
199c4f6ebcSKees Cook void lkdtm_VMALLOC_LINEAR_OVERFLOW(void)
209c4f6ebcSKees Cook {
219c4f6ebcSKees Cook 	char *one, *two;
229c4f6ebcSKees Cook 
239c4f6ebcSKees Cook 	one = vzalloc(PAGE_SIZE);
249c4f6ebcSKees Cook 	two = vzalloc(PAGE_SIZE);
259c4f6ebcSKees Cook 
269c4f6ebcSKees Cook 	pr_info("Attempting vmalloc linear overflow ...\n");
279c4f6ebcSKees Cook 	memset(one, 0xAA, PAGE_SIZE + 1);
289c4f6ebcSKees Cook 
299c4f6ebcSKees Cook 	vfree(two);
309c4f6ebcSKees Cook 	vfree(one);
319c4f6ebcSKees Cook }
329c4f6ebcSKees Cook 
339c4f6ebcSKees Cook /*
34039a1c42SKees Cook  * This tries to stay within the next largest power-of-2 kmalloc cache
35039a1c42SKees Cook  * to avoid actually overwriting anything important if it's not detected
36039a1c42SKees Cook  * correctly.
37039a1c42SKees Cook  */
389c4f6ebcSKees Cook void lkdtm_SLAB_LINEAR_OVERFLOW(void)
39039a1c42SKees Cook {
40039a1c42SKees Cook 	size_t len = 1020;
41039a1c42SKees Cook 	u32 *data = kmalloc(len, GFP_KERNEL);
42039a1c42SKees Cook 	if (!data)
43039a1c42SKees Cook 		return;
44039a1c42SKees Cook 
459c4f6ebcSKees Cook 	pr_info("Attempting slab linear overflow ...\n");
46039a1c42SKees Cook 	data[1024 / sizeof(u32)] = 0x12345678;
47039a1c42SKees Cook 	kfree(data);
48039a1c42SKees Cook }
49039a1c42SKees Cook 
50039a1c42SKees Cook void lkdtm_WRITE_AFTER_FREE(void)
51039a1c42SKees Cook {
52039a1c42SKees Cook 	int *base, *again;
53039a1c42SKees Cook 	size_t len = 1024;
54039a1c42SKees Cook 	/*
55039a1c42SKees Cook 	 * The slub allocator uses the first word to store the free
56039a1c42SKees Cook 	 * pointer in some configurations. Use the middle of the
57039a1c42SKees Cook 	 * allocation to avoid running into the freelist
58039a1c42SKees Cook 	 */
59039a1c42SKees Cook 	size_t offset = (len / sizeof(*base)) / 2;
60039a1c42SKees Cook 
61039a1c42SKees Cook 	base = kmalloc(len, GFP_KERNEL);
62039a1c42SKees Cook 	if (!base)
63039a1c42SKees Cook 		return;
64039a1c42SKees Cook 	pr_info("Allocated memory %p-%p\n", base, &base[offset * 2]);
65039a1c42SKees Cook 	pr_info("Attempting bad write to freed memory at %p\n",
66039a1c42SKees Cook 		&base[offset]);
67039a1c42SKees Cook 	kfree(base);
68039a1c42SKees Cook 	base[offset] = 0x0abcdef0;
69039a1c42SKees Cook 	/* Attempt to notice the overwrite. */
70039a1c42SKees Cook 	again = kmalloc(len, GFP_KERNEL);
71039a1c42SKees Cook 	kfree(again);
72039a1c42SKees Cook 	if (again != base)
73039a1c42SKees Cook 		pr_info("Hmm, didn't get the same memory range.\n");
74039a1c42SKees Cook }
75039a1c42SKees Cook 
76039a1c42SKees Cook void lkdtm_READ_AFTER_FREE(void)
77039a1c42SKees Cook {
78039a1c42SKees Cook 	int *base, *val, saw;
79039a1c42SKees Cook 	size_t len = 1024;
80039a1c42SKees Cook 	/*
81e12145cfSKees Cook 	 * The slub allocator will use the either the first word or
82e12145cfSKees Cook 	 * the middle of the allocation to store the free pointer,
83e12145cfSKees Cook 	 * depending on configurations. Store in the second word to
84e12145cfSKees Cook 	 * avoid running into the freelist.
85039a1c42SKees Cook 	 */
86e12145cfSKees Cook 	size_t offset = sizeof(*base);
87039a1c42SKees Cook 
88039a1c42SKees Cook 	base = kmalloc(len, GFP_KERNEL);
89039a1c42SKees Cook 	if (!base) {
90039a1c42SKees Cook 		pr_info("Unable to allocate base memory.\n");
91039a1c42SKees Cook 		return;
92039a1c42SKees Cook 	}
93039a1c42SKees Cook 
94039a1c42SKees Cook 	val = kmalloc(len, GFP_KERNEL);
95039a1c42SKees Cook 	if (!val) {
96039a1c42SKees Cook 		pr_info("Unable to allocate val memory.\n");
97039a1c42SKees Cook 		kfree(base);
98039a1c42SKees Cook 		return;
99039a1c42SKees Cook 	}
100039a1c42SKees Cook 
101039a1c42SKees Cook 	*val = 0x12345678;
102039a1c42SKees Cook 	base[offset] = *val;
103039a1c42SKees Cook 	pr_info("Value in memory before free: %x\n", base[offset]);
104039a1c42SKees Cook 
105039a1c42SKees Cook 	kfree(base);
106039a1c42SKees Cook 
107039a1c42SKees Cook 	pr_info("Attempting bad read from freed memory\n");
108039a1c42SKees Cook 	saw = base[offset];
109039a1c42SKees Cook 	if (saw != *val) {
110039a1c42SKees Cook 		/* Good! Poisoning happened, so declare a win. */
111039a1c42SKees Cook 		pr_info("Memory correctly poisoned (%x)\n", saw);
112*5b777131SKees Cook 	} else {
113*5b777131SKees Cook 		pr_err("FAIL: Memory was not poisoned!\n");
114*5b777131SKees Cook 		pr_expected_config_param(CONFIG_INIT_ON_FREE_DEFAULT_ON, "init_on_free");
115039a1c42SKees Cook 	}
116039a1c42SKees Cook 
117039a1c42SKees Cook 	kfree(val);
118039a1c42SKees Cook }
119039a1c42SKees Cook 
120039a1c42SKees Cook void lkdtm_WRITE_BUDDY_AFTER_FREE(void)
121039a1c42SKees Cook {
122039a1c42SKees Cook 	unsigned long p = __get_free_page(GFP_KERNEL);
123039a1c42SKees Cook 	if (!p) {
124039a1c42SKees Cook 		pr_info("Unable to allocate free page\n");
125039a1c42SKees Cook 		return;
126039a1c42SKees Cook 	}
127039a1c42SKees Cook 
128039a1c42SKees Cook 	pr_info("Writing to the buddy page before free\n");
129039a1c42SKees Cook 	memset((void *)p, 0x3, PAGE_SIZE);
130039a1c42SKees Cook 	free_page(p);
131039a1c42SKees Cook 	schedule();
132039a1c42SKees Cook 	pr_info("Attempting bad write to the buddy page after free\n");
133039a1c42SKees Cook 	memset((void *)p, 0x78, PAGE_SIZE);
134039a1c42SKees Cook 	/* Attempt to notice the overwrite. */
135039a1c42SKees Cook 	p = __get_free_page(GFP_KERNEL);
136039a1c42SKees Cook 	free_page(p);
137039a1c42SKees Cook 	schedule();
138039a1c42SKees Cook }
139039a1c42SKees Cook 
140039a1c42SKees Cook void lkdtm_READ_BUDDY_AFTER_FREE(void)
141039a1c42SKees Cook {
142039a1c42SKees Cook 	unsigned long p = __get_free_page(GFP_KERNEL);
143039a1c42SKees Cook 	int saw, *val;
144039a1c42SKees Cook 	int *base;
145039a1c42SKees Cook 
146039a1c42SKees Cook 	if (!p) {
147039a1c42SKees Cook 		pr_info("Unable to allocate free page\n");
148039a1c42SKees Cook 		return;
149039a1c42SKees Cook 	}
150039a1c42SKees Cook 
151039a1c42SKees Cook 	val = kmalloc(1024, GFP_KERNEL);
152039a1c42SKees Cook 	if (!val) {
153039a1c42SKees Cook 		pr_info("Unable to allocate val memory.\n");
154039a1c42SKees Cook 		free_page(p);
155039a1c42SKees Cook 		return;
156039a1c42SKees Cook 	}
157039a1c42SKees Cook 
158039a1c42SKees Cook 	base = (int *)p;
159039a1c42SKees Cook 
160039a1c42SKees Cook 	*val = 0x12345678;
161039a1c42SKees Cook 	base[0] = *val;
162039a1c42SKees Cook 	pr_info("Value in memory before free: %x\n", base[0]);
163039a1c42SKees Cook 	free_page(p);
164039a1c42SKees Cook 	pr_info("Attempting to read from freed memory\n");
165039a1c42SKees Cook 	saw = base[0];
166039a1c42SKees Cook 	if (saw != *val) {
167039a1c42SKees Cook 		/* Good! Poisoning happened, so declare a win. */
168039a1c42SKees Cook 		pr_info("Memory correctly poisoned (%x)\n", saw);
169*5b777131SKees Cook 	} else {
170*5b777131SKees Cook 		pr_err("FAIL: Buddy page was not poisoned!\n");
171*5b777131SKees Cook 		pr_expected_config_param(CONFIG_INIT_ON_FREE_DEFAULT_ON, "init_on_free");
172039a1c42SKees Cook 	}
173039a1c42SKees Cook 
174039a1c42SKees Cook 	kfree(val);
175039a1c42SKees Cook }
176966fede8SKees Cook 
177966fede8SKees Cook void lkdtm_SLAB_FREE_DOUBLE(void)
178966fede8SKees Cook {
179966fede8SKees Cook 	int *val;
180966fede8SKees Cook 
181966fede8SKees Cook 	val = kmem_cache_alloc(double_free_cache, GFP_KERNEL);
182966fede8SKees Cook 	if (!val) {
183966fede8SKees Cook 		pr_info("Unable to allocate double_free_cache memory.\n");
184966fede8SKees Cook 		return;
185966fede8SKees Cook 	}
186966fede8SKees Cook 
187966fede8SKees Cook 	/* Just make sure we got real memory. */
188966fede8SKees Cook 	*val = 0x12345678;
189966fede8SKees Cook 	pr_info("Attempting double slab free ...\n");
190966fede8SKees Cook 	kmem_cache_free(double_free_cache, val);
191966fede8SKees Cook 	kmem_cache_free(double_free_cache, val);
192966fede8SKees Cook }
193966fede8SKees Cook 
194966fede8SKees Cook void lkdtm_SLAB_FREE_CROSS(void)
195966fede8SKees Cook {
196966fede8SKees Cook 	int *val;
197966fede8SKees Cook 
198966fede8SKees Cook 	val = kmem_cache_alloc(a_cache, GFP_KERNEL);
199966fede8SKees Cook 	if (!val) {
200966fede8SKees Cook 		pr_info("Unable to allocate a_cache memory.\n");
201966fede8SKees Cook 		return;
202966fede8SKees Cook 	}
203966fede8SKees Cook 
204966fede8SKees Cook 	/* Just make sure we got real memory. */
205966fede8SKees Cook 	*val = 0x12345679;
206966fede8SKees Cook 	pr_info("Attempting cross-cache slab free ...\n");
207966fede8SKees Cook 	kmem_cache_free(b_cache, val);
208966fede8SKees Cook }
209966fede8SKees Cook 
210966fede8SKees Cook void lkdtm_SLAB_FREE_PAGE(void)
211966fede8SKees Cook {
212966fede8SKees Cook 	unsigned long p = __get_free_page(GFP_KERNEL);
213966fede8SKees Cook 
214966fede8SKees Cook 	pr_info("Attempting non-Slab slab free ...\n");
215966fede8SKees Cook 	kmem_cache_free(NULL, (void *)p);
216966fede8SKees Cook 	free_page(p);
217966fede8SKees Cook }
218966fede8SKees Cook 
219966fede8SKees Cook /*
220966fede8SKees Cook  * We have constructors to keep the caches distinctly separated without
221966fede8SKees Cook  * needing to boot with "slab_nomerge".
222966fede8SKees Cook  */
223966fede8SKees Cook static void ctor_double_free(void *region)
224966fede8SKees Cook { }
225966fede8SKees Cook static void ctor_a(void *region)
226966fede8SKees Cook { }
227966fede8SKees Cook static void ctor_b(void *region)
228966fede8SKees Cook { }
229966fede8SKees Cook 
230966fede8SKees Cook void __init lkdtm_heap_init(void)
231966fede8SKees Cook {
232966fede8SKees Cook 	double_free_cache = kmem_cache_create("lkdtm-heap-double_free",
233966fede8SKees Cook 					      64, 0, 0, ctor_double_free);
234966fede8SKees Cook 	a_cache = kmem_cache_create("lkdtm-heap-a", 64, 0, 0, ctor_a);
235966fede8SKees Cook 	b_cache = kmem_cache_create("lkdtm-heap-b", 64, 0, 0, ctor_b);
236966fede8SKees Cook }
237966fede8SKees Cook 
238966fede8SKees Cook void __exit lkdtm_heap_exit(void)
239966fede8SKees Cook {
240966fede8SKees Cook 	kmem_cache_destroy(double_free_cache);
241966fede8SKees Cook 	kmem_cache_destroy(a_cache);
242966fede8SKees Cook 	kmem_cache_destroy(b_cache);
243966fede8SKees Cook }
244