Merge tag 'copy-struct-from-user-v5.4-rc2' of git://git.kernel.org/pub/scm/linux...
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 4 Oct 2019 17:36:31 +0000 (10:36 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 4 Oct 2019 17:36:31 +0000 (10:36 -0700)
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

1  2 
include/uapi/linux/sched.h
kernel/fork.c

index 3d8f03bfd428e283178e31ef195498e6c578e467,0945805982b4b44c1dc1811fd50eeb224a34c251..99335e1f4a275b1dbc86219a6dee7701b18fd624
  #define CLONE_NEWNET          0x40000000      /* New network namespace */
  #define CLONE_IO              0x80000000      /* Clone io context */
  
 -/*
 - * Arguments for the clone3 syscall
 +#ifndef __ASSEMBLY__
 +/**
 + * struct clone_args - arguments for the clone3 syscall
 + * @flags:       Flags for the new process as listed above.
 + *               All flags are valid except for CSIGNAL and
 + *               CLONE_DETACHED.
 + * @pidfd:       If CLONE_PIDFD is set, a pidfd will be
 + *               returned in this argument.
 + * @child_tid:   If CLONE_CHILD_SETTID is set, the TID of the
 + *               child process will be returned in the child's
 + *               memory.
 + * @parent_tid:  If CLONE_PARENT_SETTID is set, the TID of
 + *               the child process will be returned in the
 + *               parent's memory.
 + * @exit_signal: The exit_signal the parent process will be
 + *               sent when the child exits.
 + * @stack:       Specify the location of the stack for the
 + *               child process.
 + * @stack_size:  The size of the stack for the child process.
 + * @tls:         If CLONE_SETTLS is set, the tls descriptor
 + *               is set to tls.
 + *
 + * The structure is versioned by size and thus extensible.
 + * New struct members must go at the end of the struct and
 + * must be properly 64bit aligned.
   */
  struct clone_args {
        __aligned_u64 flags;
        __aligned_u64 stack_size;
        __aligned_u64 tls;
  };
 +#endif
  
+ #define CLONE_ARGS_SIZE_VER0 64 /* sizeof first published struct */
  /*
   * Scheduling policies
   */
diff --combined kernel/fork.c
index bf11cf39579ae50d88ff2c9f545fa6f249b9e72e,2ef529869c6417ac20ee5cda46c48d6c55f74c0d..1f6c45f6a734dee95199e984bee662e884729a81
@@@ -2525,39 -2525,19 +2525,19 @@@ SYSCALL_DEFINE5(clone, unsigned long, c
  #ifdef __ARCH_WANT_SYS_CLONE3
  noinline static int copy_clone_args_from_user(struct kernel_clone_args *kargs,
                                              struct clone_args __user *uargs,
-                                             size_t size)
+                                             size_t usize)
  {
+       int err;
        struct clone_args args;
  
-       if (unlikely(size > PAGE_SIZE))
+       if (unlikely(usize > PAGE_SIZE))
                return -E2BIG;
-       if (unlikely(size < sizeof(struct clone_args)))
+       if (unlikely(usize < CLONE_ARGS_SIZE_VER0))
                return -EINVAL;
  
-       if (unlikely(!access_ok(uargs, size)))
-               return -EFAULT;
-       if (size > sizeof(struct clone_args)) {
-               unsigned char __user *addr;
-               unsigned char __user *end;
-               unsigned char val;
-               addr = (void __user *)uargs + sizeof(struct clone_args);
-               end = (void __user *)uargs + size;
-               for (; addr < end; addr++) {
-                       if (get_user(val, addr))
-                               return -EFAULT;
-                       if (val)
-                               return -E2BIG;
-               }
-               size = sizeof(struct clone_args);
-       }
-       if (copy_from_user(&args, uargs, size))
-               return -EFAULT;
+       err = copy_struct_from_user(&args, sizeof(args), uargs, usize);
+       if (err)
+               return err;
  
        /*
         * Verify that higher 32bits of exit_signal are unset and that
@@@ -2604,17 -2584,6 +2584,17 @@@ static bool clone3_args_valid(const str
        return true;
  }
  
 +/**
 + * clone3 - create a new process with specific properties
 + * @uargs: argument structure
 + * @size:  size of @uargs
 + *
 + * clone3() is the extensible successor to clone()/clone2().
 + * It takes a struct as argument that is versioned by its size.
 + *
 + * Return: On success, a positive PID for the child process.
 + *         On error, a negative errno number.
 + */
  SYSCALL_DEFINE2(clone3, struct clone_args __user *, uargs, size_t, size)
  {
        int err;