13e2a4c18SKees Cook /* 23e2a4c18SKees Cook * Kernel module for testing copy_to/from_user infrastructure. 33e2a4c18SKees Cook * 43e2a4c18SKees Cook * Copyright 2013 Google Inc. All Rights Reserved 53e2a4c18SKees Cook * 63e2a4c18SKees Cook * Authors: 73e2a4c18SKees Cook * Kees Cook <keescook@chromium.org> 83e2a4c18SKees Cook * 93e2a4c18SKees Cook * This software is licensed under the terms of the GNU General Public 103e2a4c18SKees Cook * License version 2, as published by the Free Software Foundation, and 113e2a4c18SKees Cook * may be copied, distributed, and modified under those terms. 123e2a4c18SKees Cook * 133e2a4c18SKees Cook * This program is distributed in the hope that it will be useful, 143e2a4c18SKees Cook * but WITHOUT ANY WARRANTY; without even the implied warranty of 153e2a4c18SKees Cook * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 163e2a4c18SKees Cook * GNU General Public License for more details. 173e2a4c18SKees Cook */ 183e2a4c18SKees Cook 193e2a4c18SKees Cook #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 203e2a4c18SKees Cook 213e2a4c18SKees Cook #include <linux/mman.h> 223e2a4c18SKees Cook #include <linux/module.h> 233e2a4c18SKees Cook #include <linux/sched.h> 243e2a4c18SKees Cook #include <linux/slab.h> 253e2a4c18SKees Cook #include <linux/uaccess.h> 263e2a4c18SKees Cook #include <linux/vmalloc.h> 273e2a4c18SKees Cook 283e2a4c18SKees Cook #define test(condition, msg) \ 293e2a4c18SKees Cook ({ \ 303e2a4c18SKees Cook int cond = (condition); \ 313e2a4c18SKees Cook if (cond) \ 323e2a4c18SKees Cook pr_warn("%s\n", msg); \ 333e2a4c18SKees Cook cond; \ 343e2a4c18SKees Cook }) 353e2a4c18SKees Cook 363e2a4c18SKees Cook static int __init test_user_copy_init(void) 373e2a4c18SKees Cook { 383e2a4c18SKees Cook int ret = 0; 393e2a4c18SKees Cook char *kmem; 403e2a4c18SKees Cook char __user *usermem; 413e2a4c18SKees Cook char *bad_usermem; 423e2a4c18SKees Cook unsigned long user_addr; 433e2a4c18SKees Cook unsigned long value = 0x5A; 443e2a4c18SKees Cook 453e2a4c18SKees Cook kmem = kmalloc(PAGE_SIZE * 2, GFP_KERNEL); 463e2a4c18SKees Cook if (!kmem) 473e2a4c18SKees Cook return -ENOMEM; 483e2a4c18SKees Cook 493e2a4c18SKees Cook user_addr = vm_mmap(NULL, 0, PAGE_SIZE * 2, 503e2a4c18SKees Cook PROT_READ | PROT_WRITE | PROT_EXEC, 513e2a4c18SKees Cook MAP_ANONYMOUS | MAP_PRIVATE, 0); 523e2a4c18SKees Cook if (user_addr >= (unsigned long)(TASK_SIZE)) { 533e2a4c18SKees Cook pr_warn("Failed to allocate user memory\n"); 543e2a4c18SKees Cook kfree(kmem); 553e2a4c18SKees Cook return -ENOMEM; 563e2a4c18SKees Cook } 573e2a4c18SKees Cook 583e2a4c18SKees Cook usermem = (char __user *)user_addr; 593e2a4c18SKees Cook bad_usermem = (char *)user_addr; 603e2a4c18SKees Cook 613e2a4c18SKees Cook /* Legitimate usage: none of these should fail. */ 623e2a4c18SKees Cook ret |= test(copy_from_user(kmem, usermem, PAGE_SIZE), 633e2a4c18SKees Cook "legitimate copy_from_user failed"); 643e2a4c18SKees Cook ret |= test(copy_to_user(usermem, kmem, PAGE_SIZE), 653e2a4c18SKees Cook "legitimate copy_to_user failed"); 663e2a4c18SKees Cook ret |= test(get_user(value, (unsigned long __user *)usermem), 673e2a4c18SKees Cook "legitimate get_user failed"); 683e2a4c18SKees Cook ret |= test(put_user(value, (unsigned long __user *)usermem), 693e2a4c18SKees Cook "legitimate put_user failed"); 703e2a4c18SKees Cook 713e2a4c18SKees Cook /* Invalid usage: none of these should succeed. */ 723e2a4c18SKees Cook ret |= test(!copy_from_user(kmem, (char __user *)(kmem + PAGE_SIZE), 733e2a4c18SKees Cook PAGE_SIZE), 743e2a4c18SKees Cook "illegal all-kernel copy_from_user passed"); 753e2a4c18SKees Cook ret |= test(!copy_from_user(bad_usermem, (char __user *)kmem, 763e2a4c18SKees Cook PAGE_SIZE), 773e2a4c18SKees Cook "illegal reversed copy_from_user passed"); 783e2a4c18SKees Cook ret |= test(!copy_to_user((char __user *)kmem, kmem + PAGE_SIZE, 793e2a4c18SKees Cook PAGE_SIZE), 803e2a4c18SKees Cook "illegal all-kernel copy_to_user passed"); 813e2a4c18SKees Cook ret |= test(!copy_to_user((char __user *)kmem, bad_usermem, 823e2a4c18SKees Cook PAGE_SIZE), 833e2a4c18SKees Cook "illegal reversed copy_to_user passed"); 843e2a4c18SKees Cook ret |= test(!get_user(value, (unsigned long __user *)kmem), 853e2a4c18SKees Cook "illegal get_user passed"); 863e2a4c18SKees Cook ret |= test(!put_user(value, (unsigned long __user *)kmem), 873e2a4c18SKees Cook "illegal put_user passed"); 883e2a4c18SKees Cook 893e2a4c18SKees Cook vm_munmap(user_addr, PAGE_SIZE * 2); 903e2a4c18SKees Cook kfree(kmem); 913e2a4c18SKees Cook 923e2a4c18SKees Cook if (ret == 0) { 933e2a4c18SKees Cook pr_info("tests passed.\n"); 943e2a4c18SKees Cook return 0; 953e2a4c18SKees Cook } 963e2a4c18SKees Cook 973e2a4c18SKees Cook return -EINVAL; 983e2a4c18SKees Cook } 993e2a4c18SKees Cook 1003e2a4c18SKees Cook module_init(test_user_copy_init); 1013e2a4c18SKees Cook 1023e2a4c18SKees Cook static void __exit test_user_copy_exit(void) 1033e2a4c18SKees Cook { 1043e2a4c18SKees Cook pr_info("unloaded.\n"); 1053e2a4c18SKees Cook } 1063e2a4c18SKees Cook 1073e2a4c18SKees Cook module_exit(test_user_copy_exit); 1083e2a4c18SKees Cook 1093e2a4c18SKees Cook MODULE_AUTHOR("Kees Cook <keescook@chromium.org>"); 1103e2a4c18SKees Cook MODULE_LICENSE("GPL"); 111