| 1 | // SPDX-License-Identifier: GPL-2.0-only |
| 2 | /* |
| 3 | * Kernel module for testing copy_to/from_user infrastructure. |
| 4 | * |
| 5 | * Copyright 2013 Google Inc. All Rights Reserved |
| 6 | * |
| 7 | * Authors: |
| 8 | * Kees Cook <keescook@chromium.org> |
| 9 | */ |
| 10 | |
| 11 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
| 12 | |
| 13 | #include <linux/mman.h> |
| 14 | #include <linux/module.h> |
| 15 | #include <linux/sched.h> |
| 16 | #include <linux/slab.h> |
| 17 | #include <linux/uaccess.h> |
| 18 | #include <kunit/test.h> |
| 19 | |
| 20 | /* |
| 21 | * Several 32-bit architectures support 64-bit {get,put}_user() calls. |
| 22 | * As there doesn't appear to be anything that can safely determine |
| 23 | * their capability at compile-time, we just have to opt-out certain archs. |
| 24 | */ |
| 25 | #if BITS_PER_LONG == 64 || (!(defined(CONFIG_ARM) && !defined(MMU)) && \ |
| 26 | !defined(CONFIG_M68K) && \ |
| 27 | !defined(CONFIG_MICROBLAZE) && \ |
| 28 | !defined(CONFIG_NIOS2) && \ |
| 29 | !defined(CONFIG_PPC32) && \ |
| 30 | !defined(CONFIG_SPARC32) && \ |
| 31 | !defined(CONFIG_SUPERH)) |
| 32 | # define TEST_U64 |
| 33 | #endif |
| 34 | |
| 35 | struct usercopy_test_priv { |
| 36 | char *kmem; |
| 37 | char __user *umem; |
| 38 | size_t size; |
| 39 | }; |
| 40 | |
| 41 | static bool is_zeroed(void *from, size_t size) |
| 42 | { |
| 43 | return memchr_inv(p: from, c: 0x0, size) == NULL; |
| 44 | } |
| 45 | |
| 46 | /* Test usage of check_nonzero_user(). */ |
| 47 | static void usercopy_test_check_nonzero_user(struct kunit *test) |
| 48 | { |
| 49 | size_t start, end, i, zero_start, zero_end; |
| 50 | struct usercopy_test_priv *priv = test->priv; |
| 51 | char __user *umem = priv->umem; |
| 52 | char *kmem = priv->kmem; |
| 53 | size_t size = priv->size; |
| 54 | |
| 55 | KUNIT_ASSERT_GE_MSG(test, size, 2 * PAGE_SIZE, "buffer too small" ); |
| 56 | |
| 57 | /* |
| 58 | * We want to cross a page boundary to exercise the code more |
| 59 | * effectively. We also don't want to make the size we scan too large, |
| 60 | * otherwise the test can take a long time and cause soft lockups. So |
| 61 | * scan a 1024 byte region across the page boundary. |
| 62 | */ |
| 63 | size = 1024; |
| 64 | start = PAGE_SIZE - (size / 2); |
| 65 | |
| 66 | kmem += start; |
| 67 | umem += start; |
| 68 | |
| 69 | zero_start = size / 4; |
| 70 | zero_end = size - zero_start; |
| 71 | |
| 72 | /* |
| 73 | * We conduct a series of check_nonzero_user() tests on a block of |
| 74 | * memory with the following byte-pattern (trying every possible |
| 75 | * [start,end] pair): |
| 76 | * |
| 77 | * [ 00 ff 00 ff ... 00 00 00 00 ... ff 00 ff 00 ] |
| 78 | * |
| 79 | * And we verify that check_nonzero_user() acts identically to |
| 80 | * memchr_inv(). |
| 81 | */ |
| 82 | |
| 83 | memset(kmem, 0x0, size); |
| 84 | for (i = 1; i < zero_start; i += 2) |
| 85 | kmem[i] = 0xff; |
| 86 | for (i = zero_end; i < size; i += 2) |
| 87 | kmem[i] = 0xff; |
| 88 | |
| 89 | KUNIT_EXPECT_EQ_MSG(test, copy_to_user(umem, kmem, size), 0, |
| 90 | "legitimate copy_to_user failed" ); |
| 91 | |
| 92 | for (start = 0; start <= size; start++) { |
| 93 | for (end = start; end <= size; end++) { |
| 94 | size_t len = end - start; |
| 95 | int retval = check_zeroed_user(from: umem + start, size: len); |
| 96 | int expected = is_zeroed(from: kmem + start, size: len); |
| 97 | |
| 98 | KUNIT_ASSERT_EQ_MSG(test, retval, expected, |
| 99 | "check_nonzero_user(=%d) != memchr_inv(=%d) mismatch (start=%zu, end=%zu)" , |
| 100 | retval, expected, start, end); |
| 101 | } |
| 102 | } |
| 103 | } |
| 104 | |
| 105 | /* Test usage of copy_struct_from_user(). */ |
| 106 | static void usercopy_test_copy_struct_from_user(struct kunit *test) |
| 107 | { |
| 108 | char *umem_src = NULL, *expected = NULL; |
| 109 | struct usercopy_test_priv *priv = test->priv; |
| 110 | char __user *umem = priv->umem; |
| 111 | char *kmem = priv->kmem; |
| 112 | size_t size = priv->size; |
| 113 | size_t ksize, usize; |
| 114 | |
| 115 | umem_src = kunit_kmalloc(test, size, GFP_KERNEL); |
| 116 | KUNIT_ASSERT_NOT_ERR_OR_NULL(test, umem_src); |
| 117 | |
| 118 | expected = kunit_kmalloc(test, size, GFP_KERNEL); |
| 119 | KUNIT_ASSERT_NOT_ERR_OR_NULL(test, expected); |
| 120 | |
| 121 | /* Fill umem with a fixed byte pattern. */ |
| 122 | memset(umem_src, 0x3e, size); |
| 123 | KUNIT_ASSERT_EQ_MSG(test, copy_to_user(umem, umem_src, size), 0, |
| 124 | "legitimate copy_to_user failed" ); |
| 125 | |
| 126 | /* Check basic case -- (usize == ksize). */ |
| 127 | ksize = size; |
| 128 | usize = size; |
| 129 | |
| 130 | memcpy(expected, umem_src, ksize); |
| 131 | |
| 132 | memset(kmem, 0x0, size); |
| 133 | KUNIT_EXPECT_EQ_MSG(test, copy_struct_from_user(kmem, ksize, umem, usize), 0, |
| 134 | "copy_struct_from_user(usize == ksize) failed" ); |
| 135 | KUNIT_EXPECT_MEMEQ_MSG(test, kmem, expected, ksize, |
| 136 | "copy_struct_from_user(usize == ksize) gives unexpected copy" ); |
| 137 | |
| 138 | /* Old userspace case -- (usize < ksize). */ |
| 139 | ksize = size; |
| 140 | usize = size / 2; |
| 141 | |
| 142 | memcpy(expected, umem_src, usize); |
| 143 | memset(expected + usize, 0x0, ksize - usize); |
| 144 | |
| 145 | memset(kmem, 0x0, size); |
| 146 | KUNIT_EXPECT_EQ_MSG(test, copy_struct_from_user(kmem, ksize, umem, usize), 0, |
| 147 | "copy_struct_from_user(usize < ksize) failed" ); |
| 148 | KUNIT_EXPECT_MEMEQ_MSG(test, kmem, expected, ksize, |
| 149 | "copy_struct_from_user(usize < ksize) gives unexpected copy" ); |
| 150 | |
| 151 | /* New userspace (-E2BIG) case -- (usize > ksize). */ |
| 152 | ksize = size / 2; |
| 153 | usize = size; |
| 154 | |
| 155 | memset(kmem, 0x0, size); |
| 156 | KUNIT_EXPECT_EQ_MSG(test, copy_struct_from_user(kmem, ksize, umem, usize), -E2BIG, |
| 157 | "copy_struct_from_user(usize > ksize) didn't give E2BIG" ); |
| 158 | |
| 159 | /* New userspace (success) case -- (usize > ksize). */ |
| 160 | ksize = size / 2; |
| 161 | usize = size; |
| 162 | |
| 163 | memcpy(expected, umem_src, ksize); |
| 164 | KUNIT_EXPECT_EQ_MSG(test, clear_user(umem + ksize, usize - ksize), 0, |
| 165 | "legitimate clear_user failed" ); |
| 166 | |
| 167 | memset(kmem, 0x0, size); |
| 168 | KUNIT_EXPECT_EQ_MSG(test, copy_struct_from_user(kmem, ksize, umem, usize), 0, |
| 169 | "copy_struct_from_user(usize > ksize) failed" ); |
| 170 | KUNIT_EXPECT_MEMEQ_MSG(test, kmem, expected, ksize, |
| 171 | "copy_struct_from_user(usize > ksize) gives unexpected copy" ); |
| 172 | } |
| 173 | |
| 174 | /* |
| 175 | * Legitimate usage: none of these copies should fail. |
| 176 | */ |
| 177 | static void usercopy_test_valid(struct kunit *test) |
| 178 | { |
| 179 | struct usercopy_test_priv *priv = test->priv; |
| 180 | char __user *usermem = priv->umem; |
| 181 | char *kmem = priv->kmem; |
| 182 | |
| 183 | memset(kmem, 0x3a, PAGE_SIZE * 2); |
| 184 | KUNIT_EXPECT_EQ_MSG(test, 0, copy_to_user(usermem, kmem, PAGE_SIZE), |
| 185 | "legitimate copy_to_user failed" ); |
| 186 | memset(kmem, 0x0, PAGE_SIZE); |
| 187 | KUNIT_EXPECT_EQ_MSG(test, 0, copy_from_user(kmem, usermem, PAGE_SIZE), |
| 188 | "legitimate copy_from_user failed" ); |
| 189 | KUNIT_EXPECT_MEMEQ_MSG(test, kmem, kmem + PAGE_SIZE, PAGE_SIZE, |
| 190 | "legitimate usercopy failed to copy data" ); |
| 191 | |
| 192 | #define test_legit(size, check) \ |
| 193 | do { \ |
| 194 | size val_##size = (check); \ |
| 195 | KUNIT_EXPECT_EQ_MSG(test, 0, \ |
| 196 | put_user(val_##size, (size __user *)usermem), \ |
| 197 | "legitimate put_user (" #size ") failed"); \ |
| 198 | val_##size = 0; \ |
| 199 | KUNIT_EXPECT_EQ_MSG(test, 0, \ |
| 200 | get_user(val_##size, (size __user *)usermem), \ |
| 201 | "legitimate get_user (" #size ") failed"); \ |
| 202 | KUNIT_EXPECT_EQ_MSG(test, val_##size, check, \ |
| 203 | "legitimate get_user (" #size ") failed to do copy"); \ |
| 204 | } while (0) |
| 205 | |
| 206 | test_legit(u8, 0x5a); |
| 207 | test_legit(u16, 0x5a5b); |
| 208 | test_legit(u32, 0x5a5b5c5d); |
| 209 | #ifdef TEST_U64 |
| 210 | test_legit(u64, 0x5a5b5c5d6a6b6c6d); |
| 211 | #endif |
| 212 | #undef test_legit |
| 213 | } |
| 214 | |
| 215 | /* |
| 216 | * Invalid usage: none of these copies should succeed. |
| 217 | */ |
| 218 | static void usercopy_test_invalid(struct kunit *test) |
| 219 | { |
| 220 | struct usercopy_test_priv *priv = test->priv; |
| 221 | char __user *usermem = priv->umem; |
| 222 | char *bad_usermem = (char *)usermem; |
| 223 | char *kmem = priv->kmem; |
| 224 | u64 *kmem_u64 = (u64 *)kmem; |
| 225 | |
| 226 | if (IS_ENABLED(CONFIG_ALTERNATE_USER_ADDRESS_SPACE) || |
| 227 | !IS_ENABLED(CONFIG_MMU)) { |
| 228 | kunit_skip(test, "Testing for kernel/userspace address confusion is only sensible on architectures with a shared address space" ); |
| 229 | return; |
| 230 | } |
| 231 | |
| 232 | /* Prepare kernel memory with check values. */ |
| 233 | memset(kmem, 0x5a, PAGE_SIZE); |
| 234 | memset(kmem + PAGE_SIZE, 0, PAGE_SIZE); |
| 235 | |
| 236 | /* Reject kernel-to-kernel copies through copy_from_user(). */ |
| 237 | KUNIT_EXPECT_NE_MSG(test, copy_from_user(kmem, (char __user *)(kmem + PAGE_SIZE), |
| 238 | PAGE_SIZE), 0, |
| 239 | "illegal all-kernel copy_from_user passed" ); |
| 240 | |
| 241 | /* Destination half of buffer should have been zeroed. */ |
| 242 | KUNIT_EXPECT_MEMEQ_MSG(test, kmem + PAGE_SIZE, kmem, PAGE_SIZE, |
| 243 | "zeroing failure for illegal all-kernel copy_from_user" ); |
| 244 | |
| 245 | #if 0 |
| 246 | /* |
| 247 | * When running with SMAP/PAN/etc, this will Oops the kernel |
| 248 | * due to the zeroing of userspace memory on failure. This needs |
| 249 | * to be tested in LKDTM instead, since this test module does not |
| 250 | * expect to explode. |
| 251 | */ |
| 252 | KUNIT_EXPECT_NE_MSG(test, copy_from_user(bad_usermem, (char __user *)kmem, |
| 253 | PAGE_SIZE), 0, |
| 254 | "illegal reversed copy_from_user passed" ); |
| 255 | #endif |
| 256 | KUNIT_EXPECT_NE_MSG(test, copy_to_user((char __user *)kmem, kmem + PAGE_SIZE, |
| 257 | PAGE_SIZE), 0, |
| 258 | "illegal all-kernel copy_to_user passed" ); |
| 259 | |
| 260 | KUNIT_EXPECT_NE_MSG(test, copy_to_user((char __user *)kmem, bad_usermem, |
| 261 | PAGE_SIZE), 0, |
| 262 | "illegal reversed copy_to_user passed" ); |
| 263 | |
| 264 | #define test_illegal(size, check) \ |
| 265 | do { \ |
| 266 | size val_##size = (check); \ |
| 267 | /* get_user() */ \ |
| 268 | KUNIT_EXPECT_NE_MSG(test, get_user(val_##size, (size __user *)kmem), 0, \ |
| 269 | "illegal get_user (" #size ") passed"); \ |
| 270 | KUNIT_EXPECT_EQ_MSG(test, val_##size, 0, \ |
| 271 | "zeroing failure for illegal get_user (" #size ")"); \ |
| 272 | /* put_user() */ \ |
| 273 | *kmem_u64 = 0xF09FA4AFF09FA4AF; \ |
| 274 | KUNIT_EXPECT_NE_MSG(test, put_user(val_##size, (size __user *)kmem), 0, \ |
| 275 | "illegal put_user (" #size ") passed"); \ |
| 276 | KUNIT_EXPECT_EQ_MSG(test, *kmem_u64, 0xF09FA4AFF09FA4AF, \ |
| 277 | "illegal put_user (" #size ") wrote to kernel memory!"); \ |
| 278 | } while (0) |
| 279 | |
| 280 | test_illegal(u8, 0x5a); |
| 281 | test_illegal(u16, 0x5a5b); |
| 282 | test_illegal(u32, 0x5a5b5c5d); |
| 283 | #ifdef TEST_U64 |
| 284 | test_illegal(u64, 0x5a5b5c5d6a6b6c6d); |
| 285 | #endif |
| 286 | #undef test_illegal |
| 287 | } |
| 288 | |
| 289 | static int usercopy_test_init(struct kunit *test) |
| 290 | { |
| 291 | struct usercopy_test_priv *priv; |
| 292 | unsigned long user_addr; |
| 293 | |
| 294 | if (!IS_ENABLED(CONFIG_MMU)) { |
| 295 | kunit_skip(test, "Userspace allocation testing not available on non-MMU systems" ); |
| 296 | return 0; |
| 297 | } |
| 298 | |
| 299 | priv = kunit_kzalloc(test, size: sizeof(*priv), GFP_KERNEL); |
| 300 | KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv); |
| 301 | test->priv = priv; |
| 302 | priv->size = PAGE_SIZE * 2; |
| 303 | |
| 304 | priv->kmem = kunit_kmalloc(test, size: priv->size, GFP_KERNEL); |
| 305 | KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->kmem); |
| 306 | |
| 307 | user_addr = kunit_vm_mmap(test, NULL, addr: 0, len: priv->size, |
| 308 | PROT_READ | PROT_WRITE | PROT_EXEC, |
| 309 | MAP_ANONYMOUS | MAP_PRIVATE, offset: 0); |
| 310 | KUNIT_ASSERT_NE_MSG(test, user_addr, 0, |
| 311 | "Could not create userspace mm" ); |
| 312 | KUNIT_ASSERT_LT_MSG(test, user_addr, (unsigned long)TASK_SIZE, |
| 313 | "Failed to allocate user memory" ); |
| 314 | priv->umem = (char __user *)user_addr; |
| 315 | |
| 316 | return 0; |
| 317 | } |
| 318 | |
| 319 | static struct kunit_case usercopy_test_cases[] = { |
| 320 | KUNIT_CASE(usercopy_test_valid), |
| 321 | KUNIT_CASE(usercopy_test_invalid), |
| 322 | KUNIT_CASE(usercopy_test_check_nonzero_user), |
| 323 | KUNIT_CASE(usercopy_test_copy_struct_from_user), |
| 324 | {} |
| 325 | }; |
| 326 | |
| 327 | static struct kunit_suite usercopy_test_suite = { |
| 328 | .name = "usercopy" , |
| 329 | .init = usercopy_test_init, |
| 330 | .test_cases = usercopy_test_cases, |
| 331 | }; |
| 332 | |
| 333 | kunit_test_suites(&usercopy_test_suite); |
| 334 | MODULE_AUTHOR("Kees Cook <kees@kernel.org>" ); |
| 335 | MODULE_DESCRIPTION("Kernel module for testing copy_to/from_user infrastructure" ); |
| 336 | MODULE_LICENSE("GPL" ); |
| 337 | |