Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1/*
  2 * This is for all the tests related to copy_to_user() and copy_from_user()
  3 * hardening.
  4 */
  5#include "lkdtm.h"
  6#include <linux/slab.h>
  7#include <linux/vmalloc.h>
  8#include <linux/mman.h>
  9#include <linux/uaccess.h>
 10#include <asm/cacheflush.h>
 11
 12/*
 13 * Many of the tests here end up using const sizes, but those would
 14 * normally be ignored by hardened usercopy, so force the compiler
 15 * into choosing the non-const path to make sure we trigger the
 16 * hardened usercopy checks by added "unconst" to all the const copies,
 17 * and making sure "cache_size" isn't optimized into a const.
 18 */
 19static volatile size_t unconst = 0;
 20static volatile size_t cache_size = 1024;
 21static struct kmem_cache *bad_cache;
 22
 23static const unsigned char test_text[] = "This is a test.\n";
 24
 25/*
 26 * Instead of adding -Wno-return-local-addr, just pass the stack address
 27 * through a function to obfuscate it from the compiler.
 28 */
 29static noinline unsigned char *trick_compiler(unsigned char *stack)
 30{
 31	return stack + 0;
 32}
 33
 34static noinline unsigned char *do_usercopy_stack_callee(int value)
 35{
 36	unsigned char buf[32];
 37	int i;
 38
 39	/* Exercise stack to avoid everything living in registers. */
 40	for (i = 0; i < sizeof(buf); i++) {
 41		buf[i] = value & 0xff;
 42	}
 43
 44	return trick_compiler(buf);
 45}
 46
 47static noinline void do_usercopy_stack(bool to_user, bool bad_frame)
 48{
 49	unsigned long user_addr;
 50	unsigned char good_stack[32];
 51	unsigned char *bad_stack;
 52	int i;
 53
 54	/* Exercise stack to avoid everything living in registers. */
 55	for (i = 0; i < sizeof(good_stack); i++)
 56		good_stack[i] = test_text[i % sizeof(test_text)];
 57
 58	/* This is a pointer to outside our current stack frame. */
 59	if (bad_frame) {
 60		bad_stack = do_usercopy_stack_callee((uintptr_t)&bad_stack);
 61	} else {
 62		/* Put start address just inside stack. */
 63		bad_stack = task_stack_page(current) + THREAD_SIZE;
 64		bad_stack -= sizeof(unsigned long);
 65	}
 66
 67	user_addr = vm_mmap(NULL, 0, PAGE_SIZE,
 68			    PROT_READ | PROT_WRITE | PROT_EXEC,
 69			    MAP_ANONYMOUS | MAP_PRIVATE, 0);
 70	if (user_addr >= TASK_SIZE) {
 71		pr_warn("Failed to allocate user memory\n");
 72		return;
 73	}
 74
 75	if (to_user) {
 76		pr_info("attempting good copy_to_user of local stack\n");
 77		if (copy_to_user((void __user *)user_addr, good_stack,
 78				 unconst + sizeof(good_stack))) {
 79			pr_warn("copy_to_user failed unexpectedly?!\n");
 80			goto free_user;
 81		}
 82
 83		pr_info("attempting bad copy_to_user of distant stack\n");
 84		if (copy_to_user((void __user *)user_addr, bad_stack,
 85				 unconst + sizeof(good_stack))) {
 86			pr_warn("copy_to_user failed, but lacked Oops\n");
 87			goto free_user;
 88		}
 89	} else {
 90		/*
 91		 * There isn't a safe way to not be protected by usercopy
 92		 * if we're going to write to another thread's stack.
 93		 */
 94		if (!bad_frame)
 95			goto free_user;
 96
 97		pr_info("attempting good copy_from_user of local stack\n");
 98		if (copy_from_user(good_stack, (void __user *)user_addr,
 99				   unconst + sizeof(good_stack))) {
100			pr_warn("copy_from_user failed unexpectedly?!\n");
101			goto free_user;
102		}
103
104		pr_info("attempting bad copy_from_user of distant stack\n");
105		if (copy_from_user(bad_stack, (void __user *)user_addr,
106				   unconst + sizeof(good_stack))) {
107			pr_warn("copy_from_user failed, but lacked Oops\n");
108			goto free_user;
109		}
110	}
111
112free_user:
113	vm_munmap(user_addr, PAGE_SIZE);
114}
115
116static void do_usercopy_heap_size(bool to_user)
117{
118	unsigned long user_addr;
119	unsigned char *one, *two;
120	size_t size = unconst + 1024;
121
122	one = kmalloc(size, GFP_KERNEL);
123	two = kmalloc(size, GFP_KERNEL);
124	if (!one || !two) {
125		pr_warn("Failed to allocate kernel memory\n");
126		goto free_kernel;
127	}
128
129	user_addr = vm_mmap(NULL, 0, PAGE_SIZE,
130			    PROT_READ | PROT_WRITE | PROT_EXEC,
131			    MAP_ANONYMOUS | MAP_PRIVATE, 0);
132	if (user_addr >= TASK_SIZE) {
133		pr_warn("Failed to allocate user memory\n");
134		goto free_kernel;
135	}
136
137	memset(one, 'A', size);
138	memset(two, 'B', size);
139
140	if (to_user) {
141		pr_info("attempting good copy_to_user of correct size\n");
142		if (copy_to_user((void __user *)user_addr, one, size)) {
143			pr_warn("copy_to_user failed unexpectedly?!\n");
144			goto free_user;
145		}
146
147		pr_info("attempting bad copy_to_user of too large size\n");
148		if (copy_to_user((void __user *)user_addr, one, 2 * size)) {
149			pr_warn("copy_to_user failed, but lacked Oops\n");
150			goto free_user;
151		}
152	} else {
153		pr_info("attempting good copy_from_user of correct size\n");
154		if (copy_from_user(one, (void __user *)user_addr, size)) {
155			pr_warn("copy_from_user failed unexpectedly?!\n");
156			goto free_user;
157		}
158
159		pr_info("attempting bad copy_from_user of too large size\n");
160		if (copy_from_user(one, (void __user *)user_addr, 2 * size)) {
161			pr_warn("copy_from_user failed, but lacked Oops\n");
162			goto free_user;
163		}
164	}
165
166free_user:
167	vm_munmap(user_addr, PAGE_SIZE);
168free_kernel:
169	kfree(one);
170	kfree(two);
171}
172
173static void do_usercopy_heap_flag(bool to_user)
174{
175	unsigned long user_addr;
176	unsigned char *good_buf = NULL;
177	unsigned char *bad_buf = NULL;
178
179	/* Make sure cache was prepared. */
180	if (!bad_cache) {
181		pr_warn("Failed to allocate kernel cache\n");
182		return;
183	}
184
185	/*
186	 * Allocate one buffer from each cache (kmalloc will have the
187	 * SLAB_USERCOPY flag already, but "bad_cache" won't).
188	 */
189	good_buf = kmalloc(cache_size, GFP_KERNEL);
190	bad_buf = kmem_cache_alloc(bad_cache, GFP_KERNEL);
191	if (!good_buf || !bad_buf) {
192		pr_warn("Failed to allocate buffers from caches\n");
193		goto free_alloc;
194	}
195
196	/* Allocate user memory we'll poke at. */
197	user_addr = vm_mmap(NULL, 0, PAGE_SIZE,
198			    PROT_READ | PROT_WRITE | PROT_EXEC,
199			    MAP_ANONYMOUS | MAP_PRIVATE, 0);
200	if (user_addr >= TASK_SIZE) {
201		pr_warn("Failed to allocate user memory\n");
202		goto free_alloc;
203	}
204
205	memset(good_buf, 'A', cache_size);
206	memset(bad_buf, 'B', cache_size);
207
208	if (to_user) {
209		pr_info("attempting good copy_to_user with SLAB_USERCOPY\n");
210		if (copy_to_user((void __user *)user_addr, good_buf,
211				 cache_size)) {
212			pr_warn("copy_to_user failed unexpectedly?!\n");
213			goto free_user;
214		}
215
216		pr_info("attempting bad copy_to_user w/o SLAB_USERCOPY\n");
217		if (copy_to_user((void __user *)user_addr, bad_buf,
218				 cache_size)) {
219			pr_warn("copy_to_user failed, but lacked Oops\n");
220			goto free_user;
221		}
222	} else {
223		pr_info("attempting good copy_from_user with SLAB_USERCOPY\n");
224		if (copy_from_user(good_buf, (void __user *)user_addr,
225				   cache_size)) {
226			pr_warn("copy_from_user failed unexpectedly?!\n");
227			goto free_user;
228		}
229
230		pr_info("attempting bad copy_from_user w/o SLAB_USERCOPY\n");
231		if (copy_from_user(bad_buf, (void __user *)user_addr,
232				   cache_size)) {
233			pr_warn("copy_from_user failed, but lacked Oops\n");
234			goto free_user;
235		}
236	}
237
238free_user:
239	vm_munmap(user_addr, PAGE_SIZE);
240free_alloc:
241	if (bad_buf)
242		kmem_cache_free(bad_cache, bad_buf);
243	kfree(good_buf);
244}
245
246/* Callable tests. */
247void lkdtm_USERCOPY_HEAP_SIZE_TO(void)
248{
249	do_usercopy_heap_size(true);
250}
251
252void lkdtm_USERCOPY_HEAP_SIZE_FROM(void)
253{
254	do_usercopy_heap_size(false);
255}
256
257void lkdtm_USERCOPY_HEAP_FLAG_TO(void)
258{
259	do_usercopy_heap_flag(true);
260}
261
262void lkdtm_USERCOPY_HEAP_FLAG_FROM(void)
263{
264	do_usercopy_heap_flag(false);
265}
266
267void lkdtm_USERCOPY_STACK_FRAME_TO(void)
268{
269	do_usercopy_stack(true, true);
270}
271
272void lkdtm_USERCOPY_STACK_FRAME_FROM(void)
273{
274	do_usercopy_stack(false, true);
275}
276
277void lkdtm_USERCOPY_STACK_BEYOND(void)
278{
279	do_usercopy_stack(true, false);
280}
281
282void lkdtm_USERCOPY_KERNEL(void)
283{
284	unsigned long user_addr;
285
286	user_addr = vm_mmap(NULL, 0, PAGE_SIZE,
287			    PROT_READ | PROT_WRITE | PROT_EXEC,
288			    MAP_ANONYMOUS | MAP_PRIVATE, 0);
289	if (user_addr >= TASK_SIZE) {
290		pr_warn("Failed to allocate user memory\n");
291		return;
292	}
293
294	pr_info("attempting good copy_to_user from kernel rodata\n");
295	if (copy_to_user((void __user *)user_addr, test_text,
296			 unconst + sizeof(test_text))) {
297		pr_warn("copy_to_user failed unexpectedly?!\n");
298		goto free_user;
299	}
300
301	pr_info("attempting bad copy_to_user from kernel text\n");
302	if (copy_to_user((void __user *)user_addr, vm_mmap,
303			 unconst + PAGE_SIZE)) {
304		pr_warn("copy_to_user failed, but lacked Oops\n");
305		goto free_user;
306	}
307
308free_user:
309	vm_munmap(user_addr, PAGE_SIZE);
310}
311
312void __init lkdtm_usercopy_init(void)
313{
314	/* Prepare cache that lacks SLAB_USERCOPY flag. */
315	bad_cache = kmem_cache_create("lkdtm-no-usercopy", cache_size, 0,
316				      0, NULL);
317}
318
319void __exit lkdtm_usercopy_exit(void)
320{
321	kmem_cache_destroy(bad_cache);
322}