hc
2024-05-10 cde9070d9970eef1f7ec2360586c802a16230ad8
kernel/lib/test_user_copy.c
....@@ -1,3 +1,4 @@
1
+// SPDX-License-Identifier: GPL-2.0-only
12 /*
23 * Kernel module for testing copy_to/from_user infrastructure.
34 *
....@@ -5,15 +6,6 @@
56 *
67 * Authors:
78 * 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.
179 */
1810
1911 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
....@@ -39,13 +31,151 @@
3931 # define TEST_U64
4032 #endif
4133
42
-#define test(condition, msg) \
43
-({ \
44
- int cond = (condition); \
45
- if (cond) \
46
- pr_warn("%s\n", msg); \
47
- cond; \
34
+#define test(condition, msg, ...) \
35
+({ \
36
+ int cond = (condition); \
37
+ if (cond) \
38
+ pr_warn("[%d] " msg "\n", __LINE__, ##__VA_ARGS__); \
39
+ cond; \
4840 })
41
+
42
+static bool is_zeroed(void *from, size_t size)
43
+{
44
+ return memchr_inv(from, 0x0, size) == NULL;
45
+}
46
+
47
+static int test_check_nonzero_user(char *kmem, char __user *umem, size_t size)
48
+{
49
+ int ret = 0;
50
+ size_t start, end, i, zero_start, zero_end;
51
+
52
+ if (test(size < 2 * PAGE_SIZE, "buffer too small"))
53
+ return -EINVAL;
54
+
55
+ /*
56
+ * We want to cross a page boundary to exercise the code more
57
+ * effectively. We also don't want to make the size we scan too large,
58
+ * otherwise the test can take a long time and cause soft lockups. So
59
+ * scan a 1024 byte region across the page boundary.
60
+ */
61
+ size = 1024;
62
+ start = PAGE_SIZE - (size / 2);
63
+
64
+ kmem += start;
65
+ umem += start;
66
+
67
+ zero_start = size / 4;
68
+ zero_end = size - zero_start;
69
+
70
+ /*
71
+ * We conduct a series of check_nonzero_user() tests on a block of
72
+ * memory with the following byte-pattern (trying every possible
73
+ * [start,end] pair):
74
+ *
75
+ * [ 00 ff 00 ff ... 00 00 00 00 ... ff 00 ff 00 ]
76
+ *
77
+ * And we verify that check_nonzero_user() acts identically to
78
+ * memchr_inv().
79
+ */
80
+
81
+ memset(kmem, 0x0, size);
82
+ for (i = 1; i < zero_start; i += 2)
83
+ kmem[i] = 0xff;
84
+ for (i = zero_end; i < size; i += 2)
85
+ kmem[i] = 0xff;
86
+
87
+ ret |= test(copy_to_user(umem, kmem, size),
88
+ "legitimate copy_to_user failed");
89
+
90
+ for (start = 0; start <= size; start++) {
91
+ for (end = start; end <= size; end++) {
92
+ size_t len = end - start;
93
+ int retval = check_zeroed_user(umem + start, len);
94
+ int expected = is_zeroed(kmem + start, len);
95
+
96
+ ret |= test(retval != expected,
97
+ "check_nonzero_user(=%d) != memchr_inv(=%d) mismatch (start=%zu, end=%zu)",
98
+ retval, expected, start, end);
99
+ }
100
+ }
101
+
102
+ return ret;
103
+}
104
+
105
+static int test_copy_struct_from_user(char *kmem, char __user *umem,
106
+ size_t size)
107
+{
108
+ int ret = 0;
109
+ char *umem_src = NULL, *expected = NULL;
110
+ size_t ksize, usize;
111
+
112
+ umem_src = kmalloc(size, GFP_KERNEL);
113
+ ret = test(umem_src == NULL, "kmalloc failed");
114
+ if (ret)
115
+ goto out_free;
116
+
117
+ expected = kmalloc(size, GFP_KERNEL);
118
+ ret = test(expected == NULL, "kmalloc failed");
119
+ if (ret)
120
+ goto out_free;
121
+
122
+ /* Fill umem with a fixed byte pattern. */
123
+ memset(umem_src, 0x3e, size);
124
+ ret |= test(copy_to_user(umem, umem_src, size),
125
+ "legitimate copy_to_user failed");
126
+
127
+ /* Check basic case -- (usize == ksize). */
128
+ ksize = size;
129
+ usize = size;
130
+
131
+ memcpy(expected, umem_src, ksize);
132
+
133
+ memset(kmem, 0x0, size);
134
+ ret |= test(copy_struct_from_user(kmem, ksize, umem, usize),
135
+ "copy_struct_from_user(usize == ksize) failed");
136
+ ret |= test(memcmp(kmem, expected, ksize),
137
+ "copy_struct_from_user(usize == ksize) gives unexpected copy");
138
+
139
+ /* Old userspace case -- (usize < ksize). */
140
+ ksize = size;
141
+ usize = size / 2;
142
+
143
+ memcpy(expected, umem_src, usize);
144
+ memset(expected + usize, 0x0, ksize - usize);
145
+
146
+ memset(kmem, 0x0, size);
147
+ ret |= test(copy_struct_from_user(kmem, ksize, umem, usize),
148
+ "copy_struct_from_user(usize < ksize) failed");
149
+ ret |= test(memcmp(kmem, expected, ksize),
150
+ "copy_struct_from_user(usize < ksize) gives unexpected copy");
151
+
152
+ /* New userspace (-E2BIG) case -- (usize > ksize). */
153
+ ksize = size / 2;
154
+ usize = size;
155
+
156
+ memset(kmem, 0x0, size);
157
+ ret |= test(copy_struct_from_user(kmem, ksize, umem, usize) != -E2BIG,
158
+ "copy_struct_from_user(usize > ksize) didn't give E2BIG");
159
+
160
+ /* New userspace (success) case -- (usize > ksize). */
161
+ ksize = size / 2;
162
+ usize = size;
163
+
164
+ memcpy(expected, umem_src, ksize);
165
+ ret |= test(clear_user(umem + ksize, usize - ksize),
166
+ "legitimate clear_user failed");
167
+
168
+ memset(kmem, 0x0, size);
169
+ ret |= test(copy_struct_from_user(kmem, ksize, umem, usize),
170
+ "copy_struct_from_user(usize > ksize) failed");
171
+ ret |= test(memcmp(kmem, expected, ksize),
172
+ "copy_struct_from_user(usize > ksize) gives unexpected copy");
173
+
174
+out_free:
175
+ kfree(expected);
176
+ kfree(umem_src);
177
+ return ret;
178
+}
49179
50180 static int __init test_user_copy_init(void)
51181 {
....@@ -114,6 +244,11 @@
114244 #endif
115245 #undef test_legit
116246
247
+ /* Test usage of check_nonzero_user(). */
248
+ ret |= test_check_nonzero_user(kmem, usermem, 2 * PAGE_SIZE);
249
+ /* Test usage of copy_struct_from_user(). */
250
+ ret |= test_copy_struct_from_user(kmem, usermem, 2 * PAGE_SIZE);
251
+
117252 /*
118253 * Invalid usage: none of these copies should succeed.
119254 */