Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge tag 'copy-struct-from-user-v5.4-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/brauner/linux

Pull copy_struct_from_user() helper from Christian Brauner:
"This contains the copy_struct_from_user() helper which got split out
from the openat2() patchset. It is a generic interface designed to
copy a struct from userspace.

The helper will be especially useful for structs versioned by size of
which we have quite a few. This allows for backwards compatibility,
i.e. an extended struct can be passed to an older kernel, or a legacy
struct can be passed to a newer kernel. For the first case (extended
struct, older kernel) the new fields in an extended struct can be set
to zero and the struct safely passed to an older kernel.

The most obvious benefit is that this helper lets us get rid of
duplicate code present in at least sched_setattr(), perf_event_open(),
and clone3(). More importantly it will also help to ensure that users
implementing versioning-by-size end up with the same core semantics.

This point is especially crucial since we have at least one case where
versioning-by-size is used but with slighly different semantics:
sched_setattr(), perf_event_open(), and clone3() all do do similar
checks to copy_struct_from_user() while rt_sigprocmask(2) always
rejects differently-sized struct arguments.

With this pull request we also switch over sched_setattr(),
perf_event_open(), and clone3() to use the new helper"

* tag 'copy-struct-from-user-v5.4-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/brauner/linux:
usercopy: Add parentheses around assignment in test_copy_struct_from_user
perf_event_open: switch to copy_struct_from_user()
sched_setattr: switch to copy_struct_from_user()
clone3: switch to copy_struct_from_user()
lib: introduce copy_struct_from_user() helper

+288 -114
+7
include/linux/bitops.h
··· 4 4 #include <asm/types.h> 5 5 #include <linux/bits.h> 6 6 7 + /* Set bits in the first 'n' bytes when loaded from memory */ 8 + #ifdef __LITTLE_ENDIAN 9 + # define aligned_byte_mask(n) ((1UL << 8*(n))-1) 10 + #else 11 + # define aligned_byte_mask(n) (~0xffUL << (BITS_PER_LONG - 8 - 8*(n))) 12 + #endif 13 + 7 14 #define BITS_PER_TYPE(type) (sizeof(type) * BITS_PER_BYTE) 8 15 #define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_TYPE(long)) 9 16
+70
include/linux/uaccess.h
··· 231 231 232 232 #endif /* ARCH_HAS_NOCACHE_UACCESS */ 233 233 234 + extern __must_check int check_zeroed_user(const void __user *from, size_t size); 235 + 236 + /** 237 + * copy_struct_from_user: copy a struct from userspace 238 + * @dst: Destination address, in kernel space. This buffer must be @ksize 239 + * bytes long. 240 + * @ksize: Size of @dst struct. 241 + * @src: Source address, in userspace. 242 + * @usize: (Alleged) size of @src struct. 243 + * 244 + * Copies a struct from userspace to kernel space, in a way that guarantees 245 + * backwards-compatibility for struct syscall arguments (as long as future 246 + * struct extensions are made such that all new fields are *appended* to the 247 + * old struct, and zeroed-out new fields have the same meaning as the old 248 + * struct). 249 + * 250 + * @ksize is just sizeof(*dst), and @usize should've been passed by userspace. 251 + * The recommended usage is something like the following: 252 + * 253 + * SYSCALL_DEFINE2(foobar, const struct foo __user *, uarg, size_t, usize) 254 + * { 255 + * int err; 256 + * struct foo karg = {}; 257 + * 258 + * if (usize > PAGE_SIZE) 259 + * return -E2BIG; 260 + * if (usize < FOO_SIZE_VER0) 261 + * return -EINVAL; 262 + * 263 + * err = copy_struct_from_user(&karg, sizeof(karg), uarg, usize); 264 + * if (err) 265 + * return err; 266 + * 267 + * // ... 268 + * } 269 + * 270 + * There are three cases to consider: 271 + * * If @usize == @ksize, then it's copied verbatim. 272 + * * If @usize < @ksize, then the userspace has passed an old struct to a 273 + * newer kernel. The rest of the trailing bytes in @dst (@ksize - @usize) 274 + * are to be zero-filled. 275 + * * If @usize > @ksize, then the userspace has passed a new struct to an 276 + * older kernel. The trailing bytes unknown to the kernel (@usize - @ksize) 277 + * are checked to ensure they are zeroed, otherwise -E2BIG is returned. 278 + * 279 + * Returns (in all cases, some data may have been copied): 280 + * * -E2BIG: (@usize > @ksize) and there are non-zero trailing bytes in @src. 281 + * * -EFAULT: access to userspace failed. 282 + */ 283 + static __always_inline __must_check int 284 + copy_struct_from_user(void *dst, size_t ksize, const void __user *src, 285 + size_t usize) 286 + { 287 + size_t size = min(ksize, usize); 288 + size_t rest = max(ksize, usize) - size; 289 + 290 + /* Deal with trailing bytes. */ 291 + if (usize < ksize) { 292 + memset(dst + size, 0, rest); 293 + } else if (usize > ksize) { 294 + int ret = check_zeroed_user(src + size, rest); 295 + if (ret <= 0) 296 + return ret ?: -E2BIG; 297 + } 298 + /* Copy the interoperable parts of the struct. */ 299 + if (copy_from_user(dst, src, size)) 300 + return -EFAULT; 301 + return 0; 302 + } 303 + 234 304 /* 235 305 * probe_kernel_read(): safely attempt to read from a location 236 306 * @dst: pointer to the buffer that shall take the data
+2
include/uapi/linux/sched.h
··· 71 71 }; 72 72 #endif 73 73 74 + #define CLONE_ARGS_SIZE_VER0 64 /* sizeof first published struct */ 75 + 74 76 /* 75 77 * Scheduling policies 76 78 */
+9 -38
kernel/events/core.c
··· 10586 10586 u32 size; 10587 10587 int ret; 10588 10588 10589 - if (!access_ok(uattr, PERF_ATTR_SIZE_VER0)) 10590 - return -EFAULT; 10591 - 10592 - /* 10593 - * zero the full structure, so that a short copy will be nice. 10594 - */ 10589 + /* Zero the full structure, so that a short copy will be nice. */ 10595 10590 memset(attr, 0, sizeof(*attr)); 10596 10591 10597 10592 ret = get_user(size, &uattr->size); 10598 10593 if (ret) 10599 10594 return ret; 10600 10595 10601 - if (size > PAGE_SIZE) /* silly large */ 10602 - goto err_size; 10603 - 10604 - if (!size) /* abi compat */ 10596 + /* ABI compatibility quirk: */ 10597 + if (!size) 10605 10598 size = PERF_ATTR_SIZE_VER0; 10606 - 10607 - if (size < PERF_ATTR_SIZE_VER0) 10599 + if (size < PERF_ATTR_SIZE_VER0 || size > PAGE_SIZE) 10608 10600 goto err_size; 10609 10601 10610 - /* 10611 - * If we're handed a bigger struct than we know of, 10612 - * ensure all the unknown bits are 0 - i.e. new 10613 - * user-space does not rely on any kernel feature 10614 - * extensions we dont know about yet. 10615 - */ 10616 - if (size > sizeof(*attr)) { 10617 - unsigned char __user *addr; 10618 - unsigned char __user *end; 10619 - unsigned char val; 10620 - 10621 - addr = (void __user *)uattr + sizeof(*attr); 10622 - end = (void __user *)uattr + size; 10623 - 10624 - for (; addr < end; addr++) { 10625 - ret = get_user(val, addr); 10626 - if (ret) 10627 - return ret; 10628 - if (val) 10629 - goto err_size; 10630 - } 10631 - size = sizeof(*attr); 10602 + ret = copy_struct_from_user(attr, sizeof(*attr), uattr, size); 10603 + if (ret) { 10604 + if (ret == -E2BIG) 10605 + goto err_size; 10606 + return ret; 10632 10607 } 10633 - 10634 - ret = copy_from_user(attr, uattr, size); 10635 - if (ret) 10636 - return -EFAULT; 10637 10608 10638 10609 attr->size = size; 10639 10610
+7 -27
kernel/fork.c
··· 2525 2525 #ifdef __ARCH_WANT_SYS_CLONE3 2526 2526 noinline static int copy_clone_args_from_user(struct kernel_clone_args *kargs, 2527 2527 struct clone_args __user *uargs, 2528 - size_t size) 2528 + size_t usize) 2529 2529 { 2530 + int err; 2530 2531 struct clone_args args; 2531 2532 2532 - if (unlikely(size > PAGE_SIZE)) 2533 + if (unlikely(usize > PAGE_SIZE)) 2533 2534 return -E2BIG; 2534 - 2535 - if (unlikely(size < sizeof(struct clone_args))) 2535 + if (unlikely(usize < CLONE_ARGS_SIZE_VER0)) 2536 2536 return -EINVAL; 2537 2537 2538 - if (unlikely(!access_ok(uargs, size))) 2539 - return -EFAULT; 2540 - 2541 - if (size > sizeof(struct clone_args)) { 2542 - unsigned char __user *addr; 2543 - unsigned char __user *end; 2544 - unsigned char val; 2545 - 2546 - addr = (void __user *)uargs + sizeof(struct clone_args); 2547 - end = (void __user *)uargs + size; 2548 - 2549 - for (; addr < end; addr++) { 2550 - if (get_user(val, addr)) 2551 - return -EFAULT; 2552 - if (val) 2553 - return -E2BIG; 2554 - } 2555 - 2556 - size = sizeof(struct clone_args); 2557 - } 2558 - 2559 - if (copy_from_user(&args, uargs, size)) 2560 - return -EFAULT; 2538 + err = copy_struct_from_user(&args, sizeof(args), uargs, usize); 2539 + if (err) 2540 + return err; 2561 2541 2562 2542 /* 2563 2543 * Verify that higher 32bits of exit_signal are unset and that
+7 -36
kernel/sched/core.c
··· 5106 5106 u32 size; 5107 5107 int ret; 5108 5108 5109 - if (!access_ok(uattr, SCHED_ATTR_SIZE_VER0)) 5110 - return -EFAULT; 5111 - 5112 5109 /* Zero the full structure, so that a short copy will be nice: */ 5113 5110 memset(attr, 0, sizeof(*attr)); 5114 5111 ··· 5113 5116 if (ret) 5114 5117 return ret; 5115 5118 5116 - /* Bail out on silly large: */ 5117 - if (size > PAGE_SIZE) 5118 - goto err_size; 5119 - 5120 5119 /* ABI compatibility quirk: */ 5121 5120 if (!size) 5122 5121 size = SCHED_ATTR_SIZE_VER0; 5123 - 5124 - if (size < SCHED_ATTR_SIZE_VER0) 5122 + if (size < SCHED_ATTR_SIZE_VER0 || size > PAGE_SIZE) 5125 5123 goto err_size; 5126 5124 5127 - /* 5128 - * If we're handed a bigger struct than we know of, 5129 - * ensure all the unknown bits are 0 - i.e. new 5130 - * user-space does not rely on any kernel feature 5131 - * extensions we dont know about yet. 5132 - */ 5133 - if (size > sizeof(*attr)) { 5134 - unsigned char __user *addr; 5135 - unsigned char __user *end; 5136 - unsigned char val; 5137 - 5138 - addr = (void __user *)uattr + sizeof(*attr); 5139 - end = (void __user *)uattr + size; 5140 - 5141 - for (; addr < end; addr++) { 5142 - ret = get_user(val, addr); 5143 - if (ret) 5144 - return ret; 5145 - if (val) 5146 - goto err_size; 5147 - } 5148 - size = sizeof(*attr); 5125 + ret = copy_struct_from_user(attr, sizeof(*attr), uattr, size); 5126 + if (ret) { 5127 + if (ret == -E2BIG) 5128 + goto err_size; 5129 + return ret; 5149 5130 } 5150 - 5151 - ret = copy_from_user(attr, uattr, size); 5152 - if (ret) 5153 - return -EFAULT; 5154 5131 5155 5132 if ((attr->sched_flags & SCHED_FLAG_UTIL_CLAMP) && 5156 5133 size < SCHED_ATTR_SIZE_VER1) ··· 5325 5354 * sys_sched_getattr - similar to sched_getparam, but with sched_attr 5326 5355 * @pid: the pid in question. 5327 5356 * @uattr: structure containing the extended parameters. 5328 - * @usize: sizeof(attr) that user-space knows about, for forwards and backwards compatibility. 5357 + * @usize: sizeof(attr) for fwd/bwd comp. 5329 5358 * @flags: for future extension. 5330 5359 */ 5331 5360 SYSCALL_DEFINE4(sched_getattr, pid_t, pid, struct sched_attr __user *, uattr,
+1 -7
lib/strnlen_user.c
··· 3 3 #include <linux/export.h> 4 4 #include <linux/uaccess.h> 5 5 #include <linux/mm.h> 6 + #include <linux/bitops.h> 6 7 7 8 #include <asm/word-at-a-time.h> 8 - 9 - /* Set bits in the first 'n' bytes when loaded from memory */ 10 - #ifdef __LITTLE_ENDIAN 11 - # define aligned_byte_mask(n) ((1ul << 8*(n))-1) 12 - #else 13 - # define aligned_byte_mask(n) (~0xfful << (BITS_PER_LONG - 8 - 8*(n))) 14 - #endif 15 9 16 10 /* 17 11 * Do a strnlen, return length of string *with* final '\0'.
+130 -6
lib/test_user_copy.c
··· 31 31 # define TEST_U64 32 32 #endif 33 33 34 - #define test(condition, msg) \ 35 - ({ \ 36 - int cond = (condition); \ 37 - if (cond) \ 38 - pr_warn("%s\n", msg); \ 39 - 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; \ 40 40 }) 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; 51 + size_t zero_start = size / 4; 52 + size_t zero_end = size - zero_start; 53 + 54 + /* 55 + * We conduct a series of check_nonzero_user() tests on a block of memory 56 + * with the following byte-pattern (trying every possible [start,end] 57 + * pair): 58 + * 59 + * [ 00 ff 00 ff ... 00 00 00 00 ... ff 00 ff 00 ] 60 + * 61 + * And we verify that check_nonzero_user() acts identically to memchr_inv(). 62 + */ 63 + 64 + memset(kmem, 0x0, size); 65 + for (i = 1; i < zero_start; i += 2) 66 + kmem[i] = 0xff; 67 + for (i = zero_end; i < size; i += 2) 68 + kmem[i] = 0xff; 69 + 70 + ret |= test(copy_to_user(umem, kmem, size), 71 + "legitimate copy_to_user failed"); 72 + 73 + for (start = 0; start <= size; start++) { 74 + for (end = start; end <= size; end++) { 75 + size_t len = end - start; 76 + int retval = check_zeroed_user(umem + start, len); 77 + int expected = is_zeroed(kmem + start, len); 78 + 79 + ret |= test(retval != expected, 80 + "check_nonzero_user(=%d) != memchr_inv(=%d) mismatch (start=%zu, end=%zu)", 81 + retval, expected, start, end); 82 + } 83 + } 84 + 85 + return ret; 86 + } 87 + 88 + static int test_copy_struct_from_user(char *kmem, char __user *umem, 89 + size_t size) 90 + { 91 + int ret = 0; 92 + char *umem_src = NULL, *expected = NULL; 93 + size_t ksize, usize; 94 + 95 + umem_src = kmalloc(size, GFP_KERNEL); 96 + if ((ret |= test(umem_src == NULL, "kmalloc failed"))) 97 + goto out_free; 98 + 99 + expected = kmalloc(size, GFP_KERNEL); 100 + if ((ret |= test(expected == NULL, "kmalloc failed"))) 101 + goto out_free; 102 + 103 + /* Fill umem with a fixed byte pattern. */ 104 + memset(umem_src, 0x3e, size); 105 + ret |= test(copy_to_user(umem, umem_src, size), 106 + "legitimate copy_to_user failed"); 107 + 108 + /* Check basic case -- (usize == ksize). */ 109 + ksize = size; 110 + usize = size; 111 + 112 + memcpy(expected, umem_src, ksize); 113 + 114 + memset(kmem, 0x0, size); 115 + ret |= test(copy_struct_from_user(kmem, ksize, umem, usize), 116 + "copy_struct_from_user(usize == ksize) failed"); 117 + ret |= test(memcmp(kmem, expected, ksize), 118 + "copy_struct_from_user(usize == ksize) gives unexpected copy"); 119 + 120 + /* Old userspace case -- (usize < ksize). */ 121 + ksize = size; 122 + usize = size / 2; 123 + 124 + memcpy(expected, umem_src, usize); 125 + memset(expected + usize, 0x0, ksize - usize); 126 + 127 + memset(kmem, 0x0, size); 128 + ret |= test(copy_struct_from_user(kmem, ksize, umem, usize), 129 + "copy_struct_from_user(usize < ksize) failed"); 130 + ret |= test(memcmp(kmem, expected, ksize), 131 + "copy_struct_from_user(usize < ksize) gives unexpected copy"); 132 + 133 + /* New userspace (-E2BIG) case -- (usize > ksize). */ 134 + ksize = size / 2; 135 + usize = size; 136 + 137 + memset(kmem, 0x0, size); 138 + ret |= test(copy_struct_from_user(kmem, ksize, umem, usize) != -E2BIG, 139 + "copy_struct_from_user(usize > ksize) didn't give E2BIG"); 140 + 141 + /* New userspace (success) case -- (usize > ksize). */ 142 + ksize = size / 2; 143 + usize = size; 144 + 145 + memcpy(expected, umem_src, ksize); 146 + ret |= test(clear_user(umem + ksize, usize - ksize), 147 + "legitimate clear_user failed"); 148 + 149 + memset(kmem, 0x0, size); 150 + ret |= test(copy_struct_from_user(kmem, ksize, umem, usize), 151 + "copy_struct_from_user(usize > ksize) failed"); 152 + ret |= test(memcmp(kmem, expected, ksize), 153 + "copy_struct_from_user(usize > ksize) gives unexpected copy"); 154 + 155 + out_free: 156 + kfree(expected); 157 + kfree(umem_src); 158 + return ret; 159 + } 41 160 42 161 static int __init test_user_copy_init(void) 43 162 { ··· 224 105 test_legit(u64, 0x5a5b5c5d6a6b6c6d); 225 106 #endif 226 107 #undef test_legit 108 + 109 + /* Test usage of check_nonzero_user(). */ 110 + ret |= test_check_nonzero_user(kmem, usermem, 2 * PAGE_SIZE); 111 + /* Test usage of copy_struct_from_user(). */ 112 + ret |= test_copy_struct_from_user(kmem, usermem, 2 * PAGE_SIZE); 227 113 228 114 /* 229 115 * Invalid usage: none of these copies should succeed.
+55
lib/usercopy.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0 2 2 #include <linux/uaccess.h> 3 + #include <linux/bitops.h> 3 4 4 5 /* out-of-line parts */ 5 6 ··· 32 31 } 33 32 EXPORT_SYMBOL(_copy_to_user); 34 33 #endif 34 + 35 + /** 36 + * check_zeroed_user: check if a userspace buffer only contains zero bytes 37 + * @from: Source address, in userspace. 38 + * @size: Size of buffer. 39 + * 40 + * This is effectively shorthand for "memchr_inv(from, 0, size) == NULL" for 41 + * userspace addresses (and is more efficient because we don't care where the 42 + * first non-zero byte is). 43 + * 44 + * Returns: 45 + * * 0: There were non-zero bytes present in the buffer. 46 + * * 1: The buffer was full of zero bytes. 47 + * * -EFAULT: access to userspace failed. 48 + */ 49 + int check_zeroed_user(const void __user *from, size_t size) 50 + { 51 + unsigned long val; 52 + uintptr_t align = (uintptr_t) from % sizeof(unsigned long); 53 + 54 + if (unlikely(size == 0)) 55 + return 1; 56 + 57 + from -= align; 58 + size += align; 59 + 60 + if (!user_access_begin(from, size)) 61 + return -EFAULT; 62 + 63 + unsafe_get_user(val, (unsigned long __user *) from, err_fault); 64 + if (align) 65 + val &= ~aligned_byte_mask(align); 66 + 67 + while (size > sizeof(unsigned long)) { 68 + if (unlikely(val)) 69 + goto done; 70 + 71 + from += sizeof(unsigned long); 72 + size -= sizeof(unsigned long); 73 + 74 + unsafe_get_user(val, (unsigned long __user *) from, err_fault); 75 + } 76 + 77 + if (size < sizeof(unsigned long)) 78 + val &= aligned_byte_mask(size); 79 + 80 + done: 81 + user_access_end(); 82 + return (val == 0); 83 + err_fault: 84 + user_access_end(); 85 + return -EFAULT; 86 + } 87 + EXPORT_SYMBOL(check_zeroed_user);