forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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 8404663 ("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 <[email protected]> Cc: Rusty Russell <[email protected]> Cc: Joe Perches <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
- Loading branch information
Showing
5 changed files
with
138 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
/* | ||
* Kernel module for testing copy_to/from_user infrastructure. | ||
* | ||
* Copyright 2013 Google Inc. All Rights Reserved | ||
* | ||
* Authors: | ||
* Kees Cook <[email protected]> | ||
* | ||
* This software is licensed under the terms of the GNU General Public | ||
* License version 2, as published by the Free Software Foundation, and | ||
* may be copied, distributed, and modified under those terms. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
*/ | ||
|
||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
|
||
#include <linux/mman.h> | ||
#include <linux/module.h> | ||
#include <linux/sched.h> | ||
#include <linux/slab.h> | ||
#include <linux/uaccess.h> | ||
#include <linux/vmalloc.h> | ||
|
||
#define test(condition, msg) \ | ||
({ \ | ||
int cond = (condition); \ | ||
if (cond) \ | ||
pr_warn("%s\n", msg); \ | ||
cond; \ | ||
}) | ||
|
||
static int __init test_user_copy_init(void) | ||
{ | ||
int ret = 0; | ||
char *kmem; | ||
char __user *usermem; | ||
char *bad_usermem; | ||
unsigned long user_addr; | ||
unsigned long value = 0x5A; | ||
|
||
kmem = kmalloc(PAGE_SIZE * 2, GFP_KERNEL); | ||
if (!kmem) | ||
return -ENOMEM; | ||
|
||
user_addr = vm_mmap(NULL, 0, PAGE_SIZE * 2, | ||
PROT_READ | PROT_WRITE | PROT_EXEC, | ||
MAP_ANONYMOUS | MAP_PRIVATE, 0); | ||
if (user_addr >= (unsigned long)(TASK_SIZE)) { | ||
pr_warn("Failed to allocate user memory\n"); | ||
kfree(kmem); | ||
return -ENOMEM; | ||
} | ||
|
||
usermem = (char __user *)user_addr; | ||
bad_usermem = (char *)user_addr; | ||
|
||
/* Legitimate usage: none of these should fail. */ | ||
ret |= test(copy_from_user(kmem, usermem, PAGE_SIZE), | ||
"legitimate copy_from_user failed"); | ||
ret |= test(copy_to_user(usermem, kmem, PAGE_SIZE), | ||
"legitimate copy_to_user failed"); | ||
ret |= test(get_user(value, (unsigned long __user *)usermem), | ||
"legitimate get_user failed"); | ||
ret |= test(put_user(value, (unsigned long __user *)usermem), | ||
"legitimate put_user failed"); | ||
|
||
/* Invalid usage: none of these should succeed. */ | ||
ret |= test(!copy_from_user(kmem, (char __user *)(kmem + PAGE_SIZE), | ||
PAGE_SIZE), | ||
"illegal all-kernel copy_from_user passed"); | ||
ret |= test(!copy_from_user(bad_usermem, (char __user *)kmem, | ||
PAGE_SIZE), | ||
"illegal reversed copy_from_user passed"); | ||
ret |= test(!copy_to_user((char __user *)kmem, kmem + PAGE_SIZE, | ||
PAGE_SIZE), | ||
"illegal all-kernel copy_to_user passed"); | ||
ret |= test(!copy_to_user((char __user *)kmem, bad_usermem, | ||
PAGE_SIZE), | ||
"illegal reversed copy_to_user passed"); | ||
ret |= test(!get_user(value, (unsigned long __user *)kmem), | ||
"illegal get_user passed"); | ||
ret |= test(!put_user(value, (unsigned long __user *)kmem), | ||
"illegal put_user passed"); | ||
|
||
vm_munmap(user_addr, PAGE_SIZE * 2); | ||
kfree(kmem); | ||
|
||
if (ret == 0) { | ||
pr_info("tests passed.\n"); | ||
return 0; | ||
} | ||
|
||
return -EINVAL; | ||
} | ||
|
||
module_init(test_user_copy_init); | ||
|
||
static void __exit test_user_copy_exit(void) | ||
{ | ||
pr_info("unloaded.\n"); | ||
} | ||
|
||
module_exit(test_user_copy_exit); | ||
|
||
MODULE_AUTHOR("Kees Cook <[email protected]>"); | ||
MODULE_LICENSE("GPL"); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# Makefile for user memory selftests | ||
|
||
# No binaries, but make sure arg-less "make" doesn't trigger "run_tests" | ||
all: | ||
|
||
run_tests: all | ||
@if /sbin/modprobe test_user_copy ; then \ | ||
rmmod test_user_copy; \ | ||
echo "user_copy: ok"; \ | ||
else \ | ||
echo "user_copy: [FAIL]"; \ | ||
exit 1; \ | ||
fi |