0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0012
0013 #include <linux/mman.h>
0014 #include <linux/module.h>
0015 #include <linux/sched.h>
0016 #include <linux/slab.h>
0017 #include <linux/uaccess.h>
0018 #include <linux/vmalloc.h>
0019
0020
0021
0022
0023
0024
0025 #if BITS_PER_LONG == 64 || (!(defined(CONFIG_ARM) && !defined(MMU)) && \
0026 !defined(CONFIG_M68K) && \
0027 !defined(CONFIG_MICROBLAZE) && \
0028 !defined(CONFIG_NIOS2) && \
0029 !defined(CONFIG_PPC32) && \
0030 !defined(CONFIG_SUPERH))
0031 # define TEST_U64
0032 #endif
0033
0034 #define test(condition, msg, ...) \
0035 ({ \
0036 int cond = (condition); \
0037 if (cond) \
0038 pr_warn("[%d] " msg "\n", __LINE__, ##__VA_ARGS__); \
0039 cond; \
0040 })
0041
0042 static bool is_zeroed(void *from, size_t size)
0043 {
0044 return memchr_inv(from, 0x0, size) == NULL;
0045 }
0046
0047 static int test_check_nonzero_user(char *kmem, char __user *umem, size_t size)
0048 {
0049 int ret = 0;
0050 size_t start, end, i, zero_start, zero_end;
0051
0052 if (test(size < 2 * PAGE_SIZE, "buffer too small"))
0053 return -EINVAL;
0054
0055
0056
0057
0058
0059
0060
0061 size = 1024;
0062 start = PAGE_SIZE - (size / 2);
0063
0064 kmem += start;
0065 umem += start;
0066
0067 zero_start = size / 4;
0068 zero_end = size - zero_start;
0069
0070
0071
0072
0073
0074
0075
0076
0077
0078
0079
0080
0081 memset(kmem, 0x0, size);
0082 for (i = 1; i < zero_start; i += 2)
0083 kmem[i] = 0xff;
0084 for (i = zero_end; i < size; i += 2)
0085 kmem[i] = 0xff;
0086
0087 ret |= test(copy_to_user(umem, kmem, size),
0088 "legitimate copy_to_user failed");
0089
0090 for (start = 0; start <= size; start++) {
0091 for (end = start; end <= size; end++) {
0092 size_t len = end - start;
0093 int retval = check_zeroed_user(umem + start, len);
0094 int expected = is_zeroed(kmem + start, len);
0095
0096 ret |= test(retval != expected,
0097 "check_nonzero_user(=%d) != memchr_inv(=%d) mismatch (start=%zu, end=%zu)",
0098 retval, expected, start, end);
0099 }
0100 }
0101
0102 return ret;
0103 }
0104
0105 static int test_copy_struct_from_user(char *kmem, char __user *umem,
0106 size_t size)
0107 {
0108 int ret = 0;
0109 char *umem_src = NULL, *expected = NULL;
0110 size_t ksize, usize;
0111
0112 umem_src = kmalloc(size, GFP_KERNEL);
0113 ret = test(umem_src == NULL, "kmalloc failed");
0114 if (ret)
0115 goto out_free;
0116
0117 expected = kmalloc(size, GFP_KERNEL);
0118 ret = test(expected == NULL, "kmalloc failed");
0119 if (ret)
0120 goto out_free;
0121
0122
0123 memset(umem_src, 0x3e, size);
0124 ret |= test(copy_to_user(umem, umem_src, size),
0125 "legitimate copy_to_user failed");
0126
0127
0128 ksize = size;
0129 usize = size;
0130
0131 memcpy(expected, umem_src, ksize);
0132
0133 memset(kmem, 0x0, size);
0134 ret |= test(copy_struct_from_user(kmem, ksize, umem, usize),
0135 "copy_struct_from_user(usize == ksize) failed");
0136 ret |= test(memcmp(kmem, expected, ksize),
0137 "copy_struct_from_user(usize == ksize) gives unexpected copy");
0138
0139
0140 ksize = size;
0141 usize = size / 2;
0142
0143 memcpy(expected, umem_src, usize);
0144 memset(expected + usize, 0x0, ksize - usize);
0145
0146 memset(kmem, 0x0, size);
0147 ret |= test(copy_struct_from_user(kmem, ksize, umem, usize),
0148 "copy_struct_from_user(usize < ksize) failed");
0149 ret |= test(memcmp(kmem, expected, ksize),
0150 "copy_struct_from_user(usize < ksize) gives unexpected copy");
0151
0152
0153 ksize = size / 2;
0154 usize = size;
0155
0156 memset(kmem, 0x0, size);
0157 ret |= test(copy_struct_from_user(kmem, ksize, umem, usize) != -E2BIG,
0158 "copy_struct_from_user(usize > ksize) didn't give E2BIG");
0159
0160
0161 ksize = size / 2;
0162 usize = size;
0163
0164 memcpy(expected, umem_src, ksize);
0165 ret |= test(clear_user(umem + ksize, usize - ksize),
0166 "legitimate clear_user failed");
0167
0168 memset(kmem, 0x0, size);
0169 ret |= test(copy_struct_from_user(kmem, ksize, umem, usize),
0170 "copy_struct_from_user(usize > ksize) failed");
0171 ret |= test(memcmp(kmem, expected, ksize),
0172 "copy_struct_from_user(usize > ksize) gives unexpected copy");
0173
0174 out_free:
0175 kfree(expected);
0176 kfree(umem_src);
0177 return ret;
0178 }
0179
0180 static int __init test_user_copy_init(void)
0181 {
0182 int ret = 0;
0183 char *kmem;
0184 char __user *usermem;
0185 char *bad_usermem;
0186 unsigned long user_addr;
0187 u8 val_u8;
0188 u16 val_u16;
0189 u32 val_u32;
0190 #ifdef TEST_U64
0191 u64 val_u64;
0192 #endif
0193
0194 kmem = kmalloc(PAGE_SIZE * 2, GFP_KERNEL);
0195 if (!kmem)
0196 return -ENOMEM;
0197
0198 user_addr = vm_mmap(NULL, 0, PAGE_SIZE * 2,
0199 PROT_READ | PROT_WRITE | PROT_EXEC,
0200 MAP_ANONYMOUS | MAP_PRIVATE, 0);
0201 if (user_addr >= (unsigned long)(TASK_SIZE)) {
0202 pr_warn("Failed to allocate user memory\n");
0203 kfree(kmem);
0204 return -ENOMEM;
0205 }
0206
0207 usermem = (char __user *)user_addr;
0208 bad_usermem = (char *)user_addr;
0209
0210
0211
0212
0213 memset(kmem, 0x3a, PAGE_SIZE * 2);
0214 ret |= test(copy_to_user(usermem, kmem, PAGE_SIZE),
0215 "legitimate copy_to_user failed");
0216 memset(kmem, 0x0, PAGE_SIZE);
0217 ret |= test(copy_from_user(kmem, usermem, PAGE_SIZE),
0218 "legitimate copy_from_user failed");
0219 ret |= test(memcmp(kmem, kmem + PAGE_SIZE, PAGE_SIZE),
0220 "legitimate usercopy failed to copy data");
0221
0222 #define test_legit(size, check) \
0223 do { \
0224 val_##size = check; \
0225 ret |= test(put_user(val_##size, (size __user *)usermem), \
0226 "legitimate put_user (" #size ") failed"); \
0227 val_##size = 0; \
0228 ret |= test(get_user(val_##size, (size __user *)usermem), \
0229 "legitimate get_user (" #size ") failed"); \
0230 ret |= test(val_##size != check, \
0231 "legitimate get_user (" #size ") failed to do copy"); \
0232 if (val_##size != check) { \
0233 pr_info("0x%llx != 0x%llx\n", \
0234 (unsigned long long)val_##size, \
0235 (unsigned long long)check); \
0236 } \
0237 } while (0)
0238
0239 test_legit(u8, 0x5a);
0240 test_legit(u16, 0x5a5b);
0241 test_legit(u32, 0x5a5b5c5d);
0242 #ifdef TEST_U64
0243 test_legit(u64, 0x5a5b5c5d6a6b6c6d);
0244 #endif
0245 #undef test_legit
0246
0247
0248 ret |= test_check_nonzero_user(kmem, usermem, 2 * PAGE_SIZE);
0249
0250 ret |= test_copy_struct_from_user(kmem, usermem, 2 * PAGE_SIZE);
0251
0252
0253
0254
0255
0256
0257 memset(kmem, 0x5a, PAGE_SIZE);
0258 memset(kmem + PAGE_SIZE, 0, PAGE_SIZE);
0259
0260
0261 ret |= test(!copy_from_user(kmem, (char __user *)(kmem + PAGE_SIZE),
0262 PAGE_SIZE),
0263 "illegal all-kernel copy_from_user passed");
0264
0265
0266 ret |= test(memcmp(kmem + PAGE_SIZE, kmem, PAGE_SIZE),
0267 "zeroing failure for illegal all-kernel copy_from_user");
0268
0269 #if 0
0270
0271
0272
0273
0274
0275
0276 ret |= test(!copy_from_user(bad_usermem, (char __user *)kmem,
0277 PAGE_SIZE),
0278 "illegal reversed copy_from_user passed");
0279 #endif
0280 ret |= test(!copy_to_user((char __user *)kmem, kmem + PAGE_SIZE,
0281 PAGE_SIZE),
0282 "illegal all-kernel copy_to_user passed");
0283 ret |= test(!copy_to_user((char __user *)kmem, bad_usermem,
0284 PAGE_SIZE),
0285 "illegal reversed copy_to_user passed");
0286
0287 #define test_illegal(size, check) \
0288 do { \
0289 val_##size = (check); \
0290 ret |= test(!get_user(val_##size, (size __user *)kmem), \
0291 "illegal get_user (" #size ") passed"); \
0292 ret |= test(val_##size != (size)0, \
0293 "zeroing failure for illegal get_user (" #size ")"); \
0294 if (val_##size != (size)0) { \
0295 pr_info("0x%llx != 0\n", \
0296 (unsigned long long)val_##size); \
0297 } \
0298 ret |= test(!put_user(val_##size, (size __user *)kmem), \
0299 "illegal put_user (" #size ") passed"); \
0300 } while (0)
0301
0302 test_illegal(u8, 0x5a);
0303 test_illegal(u16, 0x5a5b);
0304 test_illegal(u32, 0x5a5b5c5d);
0305 #ifdef TEST_U64
0306 test_illegal(u64, 0x5a5b5c5d6a6b6c6d);
0307 #endif
0308 #undef test_illegal
0309
0310 vm_munmap(user_addr, PAGE_SIZE * 2);
0311 kfree(kmem);
0312
0313 if (ret == 0) {
0314 pr_info("tests passed.\n");
0315 return 0;
0316 }
0317
0318 return -EINVAL;
0319 }
0320
0321 module_init(test_user_copy_init);
0322
0323 static void __exit test_user_copy_exit(void)
0324 {
0325 pr_info("unloaded.\n");
0326 }
0327
0328 module_exit(test_user_copy_exit);
0329
0330 MODULE_AUTHOR("Kees Cook <keescook@chromium.org>");
0331 MODULE_LICENSE("GPL");