Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

test: check copy_to/from_user boundary validation

To help avoid an architecture failing to correctly check kernel/user
boundaries when handling copy_to_user, copy_from_user, put_user, or
get_user, perform some simple tests and fail to load if any of them
behave unexpectedly.

Specifically, this is to make sure there is a way to notice if things
like what was fixed in commit 8404663f81d2 ("ARM: 7527/1: uaccess:
explicitly check __user pointer when !CPU_USE_DOMAINS") ever regresses
again, for any architecture.

Additionally, adds new "user" selftest target, which loads this module.

Signed-off-by: Kees Cook <keescook@chromium.org>
Cc: Rusty Russell <rusty@rustcorp.com.au>
Cc: Joe Perches <joe@perches.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Kees Cook and committed by
Linus Torvalds
3e2a4c18 93e9ef83

+138
+13
lib/Kconfig.debug
··· 1609 1609 1610 1610 If unsure, say N. 1611 1611 1612 + config TEST_USER_COPY 1613 + tristate "Test user/kernel boundary protections" 1614 + default n 1615 + depends on m 1616 + help 1617 + This builds the "test_user_copy" module that runs sanity checks 1618 + on the copy_to/from_user infrastructure, making sure basic 1619 + user/kernel boundary testing is working. If it fails to load, 1620 + a regression has been detected in the user/kernel memory boundary 1621 + protections. 1622 + 1623 + If unsure, say N. 1624 + 1612 1625 source "samples/Kconfig" 1613 1626 1614 1627 source "lib/Kconfig.kgdb"
+1
lib/Makefile
··· 32 32 obj-y += kstrtox.o 33 33 obj-$(CONFIG_TEST_KSTRTOX) += test-kstrtox.o 34 34 obj-$(CONFIG_TEST_MODULE) += test_module.o 35 + obj-$(CONFIG_TEST_USER_COPY) += test_user_copy.o 35 36 36 37 ifeq ($(CONFIG_DEBUG_KOBJECT),y) 37 38 CFLAGS_kobject.o += -DDEBUG
+110
lib/test_user_copy.c
··· 1 + /* 2 + * Kernel module for testing copy_to/from_user infrastructure. 3 + * 4 + * Copyright 2013 Google Inc. All Rights Reserved 5 + * 6 + * Authors: 7 + * Kees Cook <keescook@chromium.org> 8 + * 9 + * This software is licensed under the terms of the GNU General Public 10 + * License version 2, as published by the Free Software Foundation, and 11 + * may be copied, distributed, and modified under those terms. 12 + * 13 + * This program is distributed in the hope that it will be useful, 14 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 + * GNU General Public License for more details. 17 + */ 18 + 19 + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 20 + 21 + #include <linux/mman.h> 22 + #include <linux/module.h> 23 + #include <linux/sched.h> 24 + #include <linux/slab.h> 25 + #include <linux/uaccess.h> 26 + #include <linux/vmalloc.h> 27 + 28 + #define test(condition, msg) \ 29 + ({ \ 30 + int cond = (condition); \ 31 + if (cond) \ 32 + pr_warn("%s\n", msg); \ 33 + cond; \ 34 + }) 35 + 36 + static int __init test_user_copy_init(void) 37 + { 38 + int ret = 0; 39 + char *kmem; 40 + char __user *usermem; 41 + char *bad_usermem; 42 + unsigned long user_addr; 43 + unsigned long value = 0x5A; 44 + 45 + kmem = kmalloc(PAGE_SIZE * 2, GFP_KERNEL); 46 + if (!kmem) 47 + return -ENOMEM; 48 + 49 + user_addr = vm_mmap(NULL, 0, PAGE_SIZE * 2, 50 + PROT_READ | PROT_WRITE | PROT_EXEC, 51 + MAP_ANONYMOUS | MAP_PRIVATE, 0); 52 + if (user_addr >= (unsigned long)(TASK_SIZE)) { 53 + pr_warn("Failed to allocate user memory\n"); 54 + kfree(kmem); 55 + return -ENOMEM; 56 + } 57 + 58 + usermem = (char __user *)user_addr; 59 + bad_usermem = (char *)user_addr; 60 + 61 + /* Legitimate usage: none of these should fail. */ 62 + ret |= test(copy_from_user(kmem, usermem, PAGE_SIZE), 63 + "legitimate copy_from_user failed"); 64 + ret |= test(copy_to_user(usermem, kmem, PAGE_SIZE), 65 + "legitimate copy_to_user failed"); 66 + ret |= test(get_user(value, (unsigned long __user *)usermem), 67 + "legitimate get_user failed"); 68 + ret |= test(put_user(value, (unsigned long __user *)usermem), 69 + "legitimate put_user failed"); 70 + 71 + /* Invalid usage: none of these should succeed. */ 72 + ret |= test(!copy_from_user(kmem, (char __user *)(kmem + PAGE_SIZE), 73 + PAGE_SIZE), 74 + "illegal all-kernel copy_from_user passed"); 75 + ret |= test(!copy_from_user(bad_usermem, (char __user *)kmem, 76 + PAGE_SIZE), 77 + "illegal reversed copy_from_user passed"); 78 + ret |= test(!copy_to_user((char __user *)kmem, kmem + PAGE_SIZE, 79 + PAGE_SIZE), 80 + "illegal all-kernel copy_to_user passed"); 81 + ret |= test(!copy_to_user((char __user *)kmem, bad_usermem, 82 + PAGE_SIZE), 83 + "illegal reversed copy_to_user passed"); 84 + ret |= test(!get_user(value, (unsigned long __user *)kmem), 85 + "illegal get_user passed"); 86 + ret |= test(!put_user(value, (unsigned long __user *)kmem), 87 + "illegal put_user passed"); 88 + 89 + vm_munmap(user_addr, PAGE_SIZE * 2); 90 + kfree(kmem); 91 + 92 + if (ret == 0) { 93 + pr_info("tests passed.\n"); 94 + return 0; 95 + } 96 + 97 + return -EINVAL; 98 + } 99 + 100 + module_init(test_user_copy_init); 101 + 102 + static void __exit test_user_copy_exit(void) 103 + { 104 + pr_info("unloaded.\n"); 105 + } 106 + 107 + module_exit(test_user_copy_exit); 108 + 109 + MODULE_AUTHOR("Kees Cook <keescook@chromium.org>"); 110 + MODULE_LICENSE("GPL");
+1
tools/testing/selftests/Makefile
··· 9 9 TARGETS += timers 10 10 TARGETS += vm 11 11 TARGETS += powerpc 12 + TARGETS += user 12 13 13 14 all: 14 15 for TARGET in $(TARGETS); do \
+13
tools/testing/selftests/user/Makefile
··· 1 + # Makefile for user memory selftests 2 + 3 + # No binaries, but make sure arg-less "make" doesn't trigger "run_tests" 4 + all: 5 + 6 + run_tests: all 7 + @if /sbin/modprobe test_user_copy ; then \ 8 + rmmod test_user_copy; \ 9 + echo "user_copy: ok"; \ 10 + else \ 11 + echo "user_copy: [FAIL]"; \ 12 + exit 1; \ 13 + fi