aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2019-10-04 13:36:31 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2019-10-04 13:36:31 -0400
commite524d16e7e324039f2a9f82e302f0a39ac7d5812 (patch)
tree8ebd39c05da4165b74a1380d6baa5a6b5064fbaf /kernel
parentaf0622f6ae416f9ac340d6d632be9879805c294a (diff)
parent341115822f8832f0c2d8af2f7e151c4c9a77bcd1 (diff)
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
Diffstat (limited to 'kernel')
-rw-r--r--kernel/events/core.c47
-rw-r--r--kernel/fork.c34
-rw-r--r--kernel/sched/core.c43
3 files changed, 23 insertions, 101 deletions
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 4655adbbae10..3f0cb82e4fbc 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -10586,55 +10586,26 @@ static int perf_copy_attr(struct perf_event_attr __user *uattr,
10586 u32 size; 10586 u32 size;
10587 int ret; 10587 int ret;
10588 10588
10589 if (!access_ok(uattr, PERF_ATTR_SIZE_VER0)) 10589 /* Zero the full structure, so that a short copy will be nice. */
10590 return -EFAULT;
10591
10592 /*
10593 * zero the full structure, so that a short copy will be nice.
10594 */
10595 memset(attr, 0, sizeof(*attr)); 10590 memset(attr, 0, sizeof(*attr));
10596 10591
10597 ret = get_user(size, &uattr->size); 10592 ret = get_user(size, &uattr->size);
10598 if (ret) 10593 if (ret)
10599 return ret; 10594 return ret;
10600 10595
10601 if (size > PAGE_SIZE) /* silly large */ 10596 /* ABI compatibility quirk: */
10602 goto err_size; 10597 if (!size)
10603
10604 if (!size) /* abi compat */
10605 size = PERF_ATTR_SIZE_VER0; 10598 size = PERF_ATTR_SIZE_VER0;
10606 10599 if (size < PERF_ATTR_SIZE_VER0 || size > PAGE_SIZE)
10607 if (size < PERF_ATTR_SIZE_VER0)
10608 goto err_size; 10600 goto err_size;
10609 10601
10610 /* 10602 ret = copy_struct_from_user(attr, sizeof(*attr), uattr, size);
10611 * If we're handed a bigger struct than we know of, 10603 if (ret) {
10612 * ensure all the unknown bits are 0 - i.e. new 10604 if (ret == -E2BIG)
10613 * user-space does not rely on any kernel feature 10605 goto err_size;
10614 * extensions we dont know about yet. 10606 return ret;
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);
10632 } 10607 }
10633 10608
10634 ret = copy_from_user(attr, uattr, size);
10635 if (ret)
10636 return -EFAULT;
10637
10638 attr->size = size; 10609 attr->size = size;
10639 10610
10640 if (attr->__reserved_1) 10611 if (attr->__reserved_1)
diff --git a/kernel/fork.c b/kernel/fork.c
index bf11cf39579a..1f6c45f6a734 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -2525,39 +2525,19 @@ SYSCALL_DEFINE5(clone, unsigned long, clone_flags, unsigned long, newsp,
2525#ifdef __ARCH_WANT_SYS_CLONE3 2525#ifdef __ARCH_WANT_SYS_CLONE3
2526noinline static int copy_clone_args_from_user(struct kernel_clone_args *kargs, 2526noinline static int copy_clone_args_from_user(struct kernel_clone_args *kargs,
2527 struct clone_args __user *uargs, 2527 struct clone_args __user *uargs,
2528 size_t size) 2528 size_t usize)
2529{ 2529{
2530 int err;
2530 struct clone_args args; 2531 struct clone_args args;
2531 2532
2532 if (unlikely(size > PAGE_SIZE)) 2533 if (unlikely(usize > PAGE_SIZE))
2533 return -E2BIG; 2534 return -E2BIG;
2534 2535 if (unlikely(usize < CLONE_ARGS_SIZE_VER0))
2535 if (unlikely(size < sizeof(struct clone_args)))
2536 return -EINVAL; 2536 return -EINVAL;
2537 2537
2538 if (unlikely(!access_ok(uargs, size))) 2538 err = copy_struct_from_user(&args, sizeof(args), uargs, usize);
2539 return -EFAULT; 2539 if (err)
2540 2540 return err;
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;
2561 2541
2562 /* 2542 /*
2563 * Verify that higher 32bits of exit_signal are unset and that 2543 * Verify that higher 32bits of exit_signal are unset and that
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 7880f4f64d0e..dd05a378631a 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -5106,9 +5106,6 @@ static int sched_copy_attr(struct sched_attr __user *uattr, struct sched_attr *a
5106 u32 size; 5106 u32 size;
5107 int ret; 5107 int ret;
5108 5108
5109 if (!access_ok(uattr, SCHED_ATTR_SIZE_VER0))
5110 return -EFAULT;
5111
5112 /* Zero the full structure, so that a short copy will be nice: */ 5109 /* Zero the full structure, so that a short copy will be nice: */
5113 memset(attr, 0, sizeof(*attr)); 5110 memset(attr, 0, sizeof(*attr));
5114 5111
@@ -5116,45 +5113,19 @@ static int sched_copy_attr(struct sched_attr __user *uattr, struct sched_attr *a
5116 if (ret) 5113 if (ret)
5117 return ret; 5114 return ret;
5118 5115
5119 /* Bail out on silly large: */
5120 if (size > PAGE_SIZE)
5121 goto err_size;
5122
5123 /* ABI compatibility quirk: */ 5116 /* ABI compatibility quirk: */
5124 if (!size) 5117 if (!size)
5125 size = SCHED_ATTR_SIZE_VER0; 5118 size = SCHED_ATTR_SIZE_VER0;
5126 5119 if (size < SCHED_ATTR_SIZE_VER0 || size > PAGE_SIZE)
5127 if (size < SCHED_ATTR_SIZE_VER0)
5128 goto err_size; 5120 goto err_size;
5129 5121
5130 /* 5122 ret = copy_struct_from_user(attr, sizeof(*attr), uattr, size);
5131 * If we're handed a bigger struct than we know of, 5123 if (ret) {
5132 * ensure all the unknown bits are 0 - i.e. new 5124 if (ret == -E2BIG)
5133 * user-space does not rely on any kernel feature 5125 goto err_size;
5134 * extensions we dont know about yet. 5126 return ret;
5135 */
5136 if (size > sizeof(*attr)) {
5137 unsigned char __user *addr;
5138 unsigned char __user *end;
5139 unsigned char val;
5140
5141 addr = (void __user *)uattr + sizeof(*attr);
5142 end = (void __user *)uattr + size;
5143
5144 for (; addr < end; addr++) {
5145 ret = get_user(val, addr);
5146 if (ret)
5147 return ret;
5148 if (val)
5149 goto err_size;
5150 }
5151 size = sizeof(*attr);
5152 } 5127 }
5153 5128
5154 ret = copy_from_user(attr, uattr, size);
5155 if (ret)
5156 return -EFAULT;
5157
5158 if ((attr->sched_flags & SCHED_FLAG_UTIL_CLAMP) && 5129 if ((attr->sched_flags & SCHED_FLAG_UTIL_CLAMP) &&
5159 size < SCHED_ATTR_SIZE_VER1) 5130 size < SCHED_ATTR_SIZE_VER1)
5160 return -EINVAL; 5131 return -EINVAL;
@@ -5354,7 +5325,7 @@ sched_attr_copy_to_user(struct sched_attr __user *uattr,
5354 * sys_sched_getattr - similar to sched_getparam, but with sched_attr 5325 * sys_sched_getattr - similar to sched_getparam, but with sched_attr
5355 * @pid: the pid in question. 5326 * @pid: the pid in question.
5356 * @uattr: structure containing the extended parameters. 5327 * @uattr: structure containing the extended parameters.
5357 * @usize: sizeof(attr) that user-space knows about, for forwards and backwards compatibility. 5328 * @usize: sizeof(attr) for fwd/bwd comp.
5358 * @flags: for future extension. 5329 * @flags: for future extension.
5359 */ 5330 */
5360SYSCALL_DEFINE4(sched_getattr, pid_t, pid, struct sched_attr __user *, uattr, 5331SYSCALL_DEFINE4(sched_getattr, pid_t, pid, struct sched_attr __user *, uattr,