lin
2025-02-25 a02983e50ab34c3e7366b27cdeca427a327faebd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
/*
 * Kernel module for testing copy_to/from_user infrastructure.
 *
 * Copyright 2013 Google Inc. All Rights Reserved
 *
 * Authors:
 *      Kees Cook       <keescook@chromium.org>
 *
 * 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 copies 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 copies should succeed.
    */
 
   /* Reject kernel-to-kernel copies through copy_from_user(). */
   ret |= test(!copy_from_user(kmem, (char __user *)(kmem + PAGE_SIZE),
                   PAGE_SIZE),
           "illegal all-kernel copy_from_user passed");
 
#if 0
   /*
    * When running with SMAP/PAN/etc, this will Oops the kernel
    * due to the zeroing of userspace memory on failure. This needs
    * to be tested in LKDTM instead, since this test module does not
    * expect to explode.
    */
   ret |= test(!copy_from_user(bad_usermem, (char __user *)kmem,
                   PAGE_SIZE),
           "illegal reversed copy_from_user passed");
#endif
   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 <keescook@chromium.org>");
MODULE_LICENSE("GPL");