Merge branch 'x86-syscall-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 4 Sep 2017 18:18:17 +0000 (11:18 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 4 Sep 2017 18:18:17 +0000 (11:18 -0700)
Pull syscall updates from Ingo Molnar:
 "Improve the security of set_fs(): we now check the address limit on a
  number of key platforms (x86, arm, arm64) before returning to
  user-space - without adding overhead to the typical system call fast
  path"

* 'x86-syscall-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  arm64/syscalls: Check address limit on user-mode return
  arm/syscalls: Check address limit on user-mode return
  x86/syscalls: Check address limit on user-mode return

1  2 
arch/arm/include/asm/uaccess.h
arch/arm/kernel/signal.c
arch/arm64/include/asm/uaccess.h
arch/arm64/kernel/signal.c
arch/x86/include/asm/uaccess.h
include/linux/syscalls.h

index 0bf2347495f13e4db7fdc1c560b0976ec1fbd619,6cc882223e341ed2401370c864e6bd9b492726ec..87936dd5d1510572993d4bcc9b4fa1222a4dd3b2
  #include <asm/unified.h>
  #include <asm/compiler.h>
  
 -#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
 -#include <asm-generic/uaccess-unaligned.h>
 -#else
 -#define __get_user_unaligned __get_user
 -#define __put_user_unaligned __put_user
 -#endif
 -
  #include <asm/extable.h>
  
  /*
@@@ -70,6 -77,8 +70,8 @@@ static inline void set_fs(mm_segment_t 
  {
        current_thread_info()->addr_limit = fs;
        modify_domain(DOMAIN_KERNEL, fs ? DOMAIN_CLIENT : DOMAIN_MANAGER);
+       /* On user-mode return, check fs is correct */
+       set_thread_flag(TIF_FSCHECK);
  }
  
  #define segment_eq(a, b)      ((a) == (b))
@@@ -519,6 -528,7 +521,6 @@@ static inline unsigned long __must_chec
  /* These are from lib/ code, and use __get_user() and friends */
  extern long strncpy_from_user(char *dest, const char __user *src, long count);
  
 -extern __must_check long strlen_user(const char __user *str);
  extern __must_check long strnlen_user(const char __user *str, long n);
  
  #endif /* _ASMARM_UACCESS_H */
diff --combined arch/arm/kernel/signal.c
index 5814298ef0b701e61e4de018aa216009c9445d30,3a48b54c6405acf67042574856de4e3a655c5cd4..e2de50bf87425e4a55baa66ef68d1fe99e53e1f8
@@@ -14,6 -14,7 +14,7 @@@
  #include <linux/uaccess.h>
  #include <linux/tracehook.h>
  #include <linux/uprobes.h>
+ #include <linux/syscalls.h>
  
  #include <asm/elf.h>
  #include <asm/cacheflush.h>
@@@ -40,10 -41,8 +41,10 @@@ static int preserve_crunch_context(stru
        return __copy_to_user(frame, kframe, sizeof(*frame));
  }
  
 -static int restore_crunch_context(struct crunch_sigframe __user *frame)
 +static int restore_crunch_context(char __user **auxp)
  {
 +      struct crunch_sigframe __user *frame =
 +              (struct crunch_sigframe __user *)*auxp;
        char kbuf[sizeof(*frame) + 8];
        struct crunch_sigframe *kframe;
  
@@@ -54,7 -53,6 +55,7 @@@
        if (kframe->magic != CRUNCH_MAGIC ||
            kframe->size != CRUNCH_STORAGE_SIZE)
                return -1;
 +      *auxp += CRUNCH_STORAGE_SIZE;
        crunch_task_restore(current_thread_info(), &kframe->storage);
        return 0;
  }
  
  #ifdef CONFIG_IWMMXT
  
 -static int preserve_iwmmxt_context(struct iwmmxt_sigframe *frame)
 +static int preserve_iwmmxt_context(struct iwmmxt_sigframe __user *frame)
  {
        char kbuf[sizeof(*frame) + 8];
        struct iwmmxt_sigframe *kframe;
 +      int err = 0;
  
        /* the iWMMXt context must be 64 bit aligned */
        kframe = (struct iwmmxt_sigframe *)((unsigned long)(kbuf + 8) & ~7);
 -      kframe->magic = IWMMXT_MAGIC;
 -      kframe->size = IWMMXT_STORAGE_SIZE;
 -      iwmmxt_task_copy(current_thread_info(), &kframe->storage);
 -      return __copy_to_user(frame, kframe, sizeof(*frame));
 +
 +      if (test_thread_flag(TIF_USING_IWMMXT)) {
 +              kframe->magic = IWMMXT_MAGIC;
 +              kframe->size = IWMMXT_STORAGE_SIZE;
 +              iwmmxt_task_copy(current_thread_info(), &kframe->storage);
 +
 +              err = __copy_to_user(frame, kframe, sizeof(*frame));
 +      } else {
 +              /*
 +               * For bug-compatibility with older kernels, some space
 +               * has to be reserved for iWMMXt even if it's not used.
 +               * Set the magic and size appropriately so that properly
 +               * written userspace can skip it reliably:
 +               */
 +              __put_user_error(DUMMY_MAGIC, &frame->magic, err);
 +              __put_user_error(IWMMXT_STORAGE_SIZE, &frame->size, err);
 +      }
 +
 +      return err;
  }
  
 -static int restore_iwmmxt_context(struct iwmmxt_sigframe *frame)
 +static int restore_iwmmxt_context(char __user **auxp)
  {
 +      struct iwmmxt_sigframe __user *frame =
 +              (struct iwmmxt_sigframe __user *)*auxp;
        char kbuf[sizeof(*frame) + 8];
        struct iwmmxt_sigframe *kframe;
  
        kframe = (struct iwmmxt_sigframe *)((unsigned long)(kbuf + 8) & ~7);
        if (__copy_from_user(kframe, frame, sizeof(*frame)))
                return -1;
 -      if (kframe->magic != IWMMXT_MAGIC ||
 -          kframe->size != IWMMXT_STORAGE_SIZE)
 +
 +      /*
 +       * For non-iWMMXt threads: a single iwmmxt_sigframe-sized dummy
 +       * block is discarded for compatibility with setup_sigframe() if
 +       * present, but we don't mandate its presence.  If some other
 +       * magic is here, it's not for us:
 +       */
 +      if (!test_thread_flag(TIF_USING_IWMMXT) &&
 +          kframe->magic != DUMMY_MAGIC)
 +              return 0;
 +
 +      if (kframe->size != IWMMXT_STORAGE_SIZE)
                return -1;
 -      iwmmxt_task_restore(current_thread_info(), &kframe->storage);
 +
 +      if (test_thread_flag(TIF_USING_IWMMXT)) {
 +              if (kframe->magic != IWMMXT_MAGIC)
 +                      return -1;
 +
 +              iwmmxt_task_restore(current_thread_info(), &kframe->storage);
 +      }
 +
 +      *auxp += IWMMXT_STORAGE_SIZE;
        return 0;
  }
  
@@@ -146,10 -108,8 +147,10 @@@ static int preserve_vfp_context(struct 
        return vfp_preserve_user_clear_hwstate(&frame->ufp, &frame->ufp_exc);
  }
  
 -static int restore_vfp_context(struct vfp_sigframe __user *frame)
 +static int restore_vfp_context(char __user **auxp)
  {
 +      struct vfp_sigframe __user *frame =
 +              (struct vfp_sigframe __user *)*auxp;
        unsigned long magic;
        unsigned long size;
        int err = 0;
        if (magic != VFP_MAGIC || size != VFP_STORAGE_SIZE)
                return -EINVAL;
  
 +      *auxp += size;
        return vfp_restore_user_hwstate(&frame->ufp, &frame->ufp_exc);
  }
  
@@@ -183,7 -142,7 +184,7 @@@ struct rt_sigframe 
  
  static int restore_sigframe(struct pt_regs *regs, struct sigframe __user *sf)
  {
 -      struct aux_sigframe __user *aux;
 +      char __user *aux;
        sigset_t set;
        int err;
  
  
        err |= !valid_user_regs(regs);
  
 -      aux = (struct aux_sigframe __user *) sf->uc.uc_regspace;
 +      aux = (char __user *) sf->uc.uc_regspace;
  #ifdef CONFIG_CRUNCH
        if (err == 0)
 -              err |= restore_crunch_context(&aux->crunch);
 +              err |= restore_crunch_context(&aux);
  #endif
  #ifdef CONFIG_IWMMXT
 -      if (err == 0 && test_thread_flag(TIF_USING_IWMMXT))
 -              err |= restore_iwmmxt_context(&aux->iwmmxt);
 +      if (err == 0)
 +              err |= restore_iwmmxt_context(&aux);
  #endif
  #ifdef CONFIG_VFP
        if (err == 0)
 -              err |= restore_vfp_context(&aux->vfp);
 +              err |= restore_vfp_context(&aux);
  #endif
  
        return err;
@@@ -328,7 -287,7 +329,7 @@@ setup_sigframe(struct sigframe __user *
                err |= preserve_crunch_context(&aux->crunch);
  #endif
  #ifdef CONFIG_IWMMXT
 -      if (err == 0 && test_thread_flag(TIF_USING_IWMMXT))
 +      if (err == 0)
                err |= preserve_iwmmxt_context(&aux->iwmmxt);
  #endif
  #ifdef CONFIG_VFP
@@@ -613,6 -572,10 +614,10 @@@ do_work_pending(struct pt_regs *regs, u
         * Update the trace code with the current status.
         */
        trace_hardirqs_off();
+       /* Check valid user FS if needed */
+       addr_limit_user_check();
        do {
                if (likely(thread_flags & _TIF_NEED_RESCHED)) {
                        schedule();
index fab46a0ea223d74781464a2a3904a4d2eac0a423,ced7a7c2dd41c7572dc108e56edfad12814a1ae4..a801a48a7972aca80f101d645f4ae9523dc9b435
@@@ -45,6 -45,9 +45,9 @@@ static inline void set_fs(mm_segment_t 
  {
        current_thread_info()->addr_limit = fs;
  
+       /* On user-mode return, check fs is correct */
+       set_thread_flag(TIF_FSCHECK);
        /*
         * Enable/disable UAO so that copy_to_user() etc can access
         * kernel memory with the unprivileged instructions.
@@@ -69,7 -72,7 +72,7 @@@
   */
  #define __range_ok(addr, size)                                                \
  ({                                                                    \
 -      unsigned long __addr = (unsigned long __force)(addr);           \
 +      unsigned long __addr = (unsigned long)(addr);                   \
        unsigned long flag, roksum;                                     \
        __chk_user_ptr(addr);                                           \
        asm("adds %1, %1, %3; ccmp %1, %4, #2, cc; cset %0, ls"         \
@@@ -254,6 -257,8 +257,6 @@@ do {                                                                       
        (void)0;                                                        \
  })
  
 -#define __get_user_unaligned __get_user
 -
  #define get_user(x, ptr)                                              \
  ({                                                                    \
        __typeof__(*(ptr)) __user *__p = (ptr);                         \
@@@ -318,6 -323,8 +321,6 @@@ do {                                                                       
        (void)0;                                                        \
  })
  
 -#define __put_user_unaligned __put_user
 -
  #define put_user(x, ptr)                                              \
  ({                                                                    \
        __typeof__(*(ptr)) __user *__p = (ptr);                         \
@@@ -345,6 -352,7 +348,6 @@@ static inline unsigned long __must_chec
  
  extern long strncpy_from_user(char *dest, const char __user *src, long count);
  
 -extern __must_check long strlen_user(const char __user *str);
  extern __must_check long strnlen_user(const char __user *str, long n);
  
  #endif /* __ASM_UACCESS_H */
index 089c3747995dc40b726fbe9f53450ddb5c350f95,0f0279148bdc08a62f10f142c598b3ce9e144025..e3e3293d1123957d9529160f068f2cc07b69705e
  
  #include <linux/compat.h>
  #include <linux/errno.h>
 +#include <linux/kernel.h>
  #include <linux/signal.h>
  #include <linux/personality.h>
  #include <linux/freezer.h>
 +#include <linux/stddef.h>
  #include <linux/uaccess.h>
 +#include <linux/sizes.h>
 +#include <linux/string.h>
  #include <linux/tracehook.h>
  #include <linux/ratelimit.h>
+ #include <linux/syscalls.h>
  
  #include <asm/debug-monitors.h>
  #include <asm/elf.h>
  struct rt_sigframe {
        struct siginfo info;
        struct ucontext uc;
 +};
 +
 +struct frame_record {
        u64 fp;
        u64 lr;
  };
  
 +struct rt_sigframe_user_layout {
 +      struct rt_sigframe __user *sigframe;
 +      struct frame_record __user *next_frame;
 +
 +      unsigned long size;     /* size of allocated sigframe data */
 +      unsigned long limit;    /* largest allowed size */
 +
 +      unsigned long fpsimd_offset;
 +      unsigned long esr_offset;
 +      unsigned long extra_offset;
 +      unsigned long end_offset;
 +};
 +
 +#define BASE_SIGFRAME_SIZE round_up(sizeof(struct rt_sigframe), 16)
 +#define TERMINATOR_SIZE round_up(sizeof(struct _aarch64_ctx), 16)
 +#define EXTRA_CONTEXT_SIZE round_up(sizeof(struct extra_context), 16)
 +
 +static void init_user_layout(struct rt_sigframe_user_layout *user)
 +{
 +      const size_t reserved_size =
 +              sizeof(user->sigframe->uc.uc_mcontext.__reserved);
 +
 +      memset(user, 0, sizeof(*user));
 +      user->size = offsetof(struct rt_sigframe, uc.uc_mcontext.__reserved);
 +
 +      user->limit = user->size + reserved_size;
 +
 +      user->limit -= TERMINATOR_SIZE;
 +      user->limit -= EXTRA_CONTEXT_SIZE;
 +      /* Reserve space for extension and terminator ^ */
 +}
 +
 +static size_t sigframe_size(struct rt_sigframe_user_layout const *user)
 +{
 +      return round_up(max(user->size, sizeof(struct rt_sigframe)), 16);
 +}
 +
 +/*
 + * Sanity limit on the approximate maximum size of signal frame we'll
 + * try to generate.  Stack alignment padding and the frame record are
 + * not taken into account.  This limit is not a guarantee and is
 + * NOT ABI.
 + */
 +#define SIGFRAME_MAXSZ SZ_64K
 +
 +static int __sigframe_alloc(struct rt_sigframe_user_layout *user,
 +                          unsigned long *offset, size_t size, bool extend)
 +{
 +      size_t padded_size = round_up(size, 16);
 +
 +      if (padded_size > user->limit - user->size &&
 +          !user->extra_offset &&
 +          extend) {
 +              int ret;
 +
 +              user->limit += EXTRA_CONTEXT_SIZE;
 +              ret = __sigframe_alloc(user, &user->extra_offset,
 +                                     sizeof(struct extra_context), false);
 +              if (ret) {
 +                      user->limit -= EXTRA_CONTEXT_SIZE;
 +                      return ret;
 +              }
 +
 +              /* Reserve space for the __reserved[] terminator */
 +              user->size += TERMINATOR_SIZE;
 +
 +              /*
 +               * Allow expansion up to SIGFRAME_MAXSZ, ensuring space for
 +               * the terminator:
 +               */
 +              user->limit = SIGFRAME_MAXSZ - TERMINATOR_SIZE;
 +      }
 +
 +      /* Still not enough space?  Bad luck! */
 +      if (padded_size > user->limit - user->size)
 +              return -ENOMEM;
 +
 +      *offset = user->size;
 +      user->size += padded_size;
 +
 +      return 0;
 +}
 +
 +/*
 + * Allocate space for an optional record of <size> bytes in the user
 + * signal frame.  The offset from the signal frame base address to the
 + * allocated block is assigned to *offset.
 + */
 +static int sigframe_alloc(struct rt_sigframe_user_layout *user,
 +                        unsigned long *offset, size_t size)
 +{
 +      return __sigframe_alloc(user, offset, size, true);
 +}
 +
 +/* Allocate the null terminator record and prevent further allocations */
 +static int sigframe_alloc_end(struct rt_sigframe_user_layout *user)
 +{
 +      int ret;
 +
 +      /* Un-reserve the space reserved for the terminator: */
 +      user->limit += TERMINATOR_SIZE;
 +
 +      ret = sigframe_alloc(user, &user->end_offset,
 +                           sizeof(struct _aarch64_ctx));
 +      if (ret)
 +              return ret;
 +
 +      /* Prevent further allocation: */
 +      user->limit = user->size;
 +      return 0;
 +}
 +
 +static void __user *apply_user_offset(
 +      struct rt_sigframe_user_layout const *user, unsigned long offset)
 +{
 +      char __user *base = (char __user *)user->sigframe;
 +
 +      return base + offset;
 +}
 +
  static int preserve_fpsimd_context(struct fpsimd_context __user *ctx)
  {
        struct fpsimd_state *fpsimd = &current->thread.fpsimd_state;
@@@ -219,159 -93,12 +220,159 @@@ static int restore_fpsimd_context(struc
        return err ? -EFAULT : 0;
  }
  
 +struct user_ctxs {
 +      struct fpsimd_context __user *fpsimd;
 +};
 +
 +static int parse_user_sigframe(struct user_ctxs *user,
 +                             struct rt_sigframe __user *sf)
 +{
 +      struct sigcontext __user *const sc = &sf->uc.uc_mcontext;
 +      struct _aarch64_ctx __user *head;
 +      char __user *base = (char __user *)&sc->__reserved;
 +      size_t offset = 0;
 +      size_t limit = sizeof(sc->__reserved);
 +      bool have_extra_context = false;
 +      char const __user *const sfp = (char const __user *)sf;
 +
 +      user->fpsimd = NULL;
 +
 +      if (!IS_ALIGNED((unsigned long)base, 16))
 +              goto invalid;
 +
 +      while (1) {
 +              int err = 0;
 +              u32 magic, size;
 +              char const __user *userp;
 +              struct extra_context const __user *extra;
 +              u64 extra_datap;
 +              u32 extra_size;
 +              struct _aarch64_ctx const __user *end;
 +              u32 end_magic, end_size;
 +
 +              if (limit - offset < sizeof(*head))
 +                      goto invalid;
 +
 +              if (!IS_ALIGNED(offset, 16))
 +                      goto invalid;
 +
 +              head = (struct _aarch64_ctx __user *)(base + offset);
 +              __get_user_error(magic, &head->magic, err);
 +              __get_user_error(size, &head->size, err);
 +              if (err)
 +                      return err;
 +
 +              if (limit - offset < size)
 +                      goto invalid;
 +
 +              switch (magic) {
 +              case 0:
 +                      if (size)
 +                              goto invalid;
 +
 +                      goto done;
 +
 +              case FPSIMD_MAGIC:
 +                      if (user->fpsimd)
 +                              goto invalid;
 +
 +                      if (size < sizeof(*user->fpsimd))
 +                              goto invalid;
 +
 +                      user->fpsimd = (struct fpsimd_context __user *)head;
 +                      break;
 +
 +              case ESR_MAGIC:
 +                      /* ignore */
 +                      break;
 +
 +              case EXTRA_MAGIC:
 +                      if (have_extra_context)
 +                              goto invalid;
 +
 +                      if (size < sizeof(*extra))
 +                              goto invalid;
 +
 +                      userp = (char const __user *)head;
 +
 +                      extra = (struct extra_context const __user *)userp;
 +                      userp += size;
 +
 +                      __get_user_error(extra_datap, &extra->datap, err);
 +                      __get_user_error(extra_size, &extra->size, err);
 +                      if (err)
 +                              return err;
 +
 +                      /* Check for the dummy terminator in __reserved[]: */
 +
 +                      if (limit - offset - size < TERMINATOR_SIZE)
 +                              goto invalid;
 +
 +                      end = (struct _aarch64_ctx const __user *)userp;
 +                      userp += TERMINATOR_SIZE;
 +
 +                      __get_user_error(end_magic, &end->magic, err);
 +                      __get_user_error(end_size, &end->size, err);
 +                      if (err)
 +                              return err;
 +
 +                      if (end_magic || end_size)
 +                              goto invalid;
 +
 +                      /* Prevent looping/repeated parsing of extra_context */
 +                      have_extra_context = true;
 +
 +                      base = (__force void __user *)extra_datap;
 +                      if (!IS_ALIGNED((unsigned long)base, 16))
 +                              goto invalid;
 +
 +                      if (!IS_ALIGNED(extra_size, 16))
 +                              goto invalid;
 +
 +                      if (base != userp)
 +                              goto invalid;
 +
 +                      /* Reject "unreasonably large" frames: */
 +                      if (extra_size > sfp + SIGFRAME_MAXSZ - userp)
 +                              goto invalid;
 +
 +                      /*
 +                       * Ignore trailing terminator in __reserved[]
 +                       * and start parsing extra data:
 +                       */
 +                      offset = 0;
 +                      limit = extra_size;
 +                      continue;
 +
 +              default:
 +                      goto invalid;
 +              }
 +
 +              if (size < sizeof(*head))
 +                      goto invalid;
 +
 +              if (limit - offset < size)
 +                      goto invalid;
 +
 +              offset += size;
 +      }
 +
 +done:
 +      if (!user->fpsimd)
 +              goto invalid;
 +
 +      return 0;
 +
 +invalid:
 +      return -EINVAL;
 +}
 +
  static int restore_sigframe(struct pt_regs *regs,
                            struct rt_sigframe __user *sf)
  {
        sigset_t set;
        int i, err;
 -      void *aux = sf->uc.uc_mcontext.__reserved;
 +      struct user_ctxs user;
  
        err = __copy_from_user(&set, &sf->uc.uc_sigmask, sizeof(set));
        if (err == 0)
        regs->syscallno = ~0UL;
  
        err |= !valid_user_regs(&regs->user_regs, current);
 +      if (err == 0)
 +              err = parse_user_sigframe(&user, sf);
  
 -      if (err == 0) {
 -              struct fpsimd_context *fpsimd_ctx =
 -                      container_of(aux, struct fpsimd_context, head);
 -              err |= restore_fpsimd_context(fpsimd_ctx);
 -      }
 +      if (err == 0)
 +              err = restore_fpsimd_context(user.fpsimd);
  
        return err;
  }
@@@ -435,37 -163,16 +436,37 @@@ badframe
        return 0;
  }
  
 -static int setup_sigframe(struct rt_sigframe __user *sf,
 +/* Determine the layout of optional records in the signal frame */
 +static int setup_sigframe_layout(struct rt_sigframe_user_layout *user)
 +{
 +      int err;
 +
 +      err = sigframe_alloc(user, &user->fpsimd_offset,
 +                           sizeof(struct fpsimd_context));
 +      if (err)
 +              return err;
 +
 +      /* fault information, if valid */
 +      if (current->thread.fault_code) {
 +              err = sigframe_alloc(user, &user->esr_offset,
 +                                   sizeof(struct esr_context));
 +              if (err)
 +                      return err;
 +      }
 +
 +      return sigframe_alloc_end(user);
 +}
 +
 +
 +static int setup_sigframe(struct rt_sigframe_user_layout *user,
                          struct pt_regs *regs, sigset_t *set)
  {
        int i, err = 0;
 -      void *aux = sf->uc.uc_mcontext.__reserved;
 -      struct _aarch64_ctx *end;
 +      struct rt_sigframe __user *sf = user->sigframe;
  
        /* set up the stack frame for unwinding */
 -      __put_user_error(regs->regs[29], &sf->fp, err);
 -      __put_user_error(regs->regs[30], &sf->lr, err);
 +      __put_user_error(regs->regs[29], &user->next_frame->fp, err);
 +      __put_user_error(regs->regs[30], &user->next_frame->lr, err);
  
        for (i = 0; i < 31; i++)
                __put_user_error(regs->regs[i], &sf->uc.uc_mcontext.regs[i],
        err |= __copy_to_user(&sf->uc.uc_sigmask, set, sizeof(*set));
  
        if (err == 0) {
 -              struct fpsimd_context *fpsimd_ctx =
 -                      container_of(aux, struct fpsimd_context, head);
 +              struct fpsimd_context __user *fpsimd_ctx =
 +                      apply_user_offset(user, user->fpsimd_offset);
                err |= preserve_fpsimd_context(fpsimd_ctx);
 -              aux += sizeof(*fpsimd_ctx);
        }
  
        /* fault information, if valid */
 -      if (current->thread.fault_code) {
 -              struct esr_context *esr_ctx =
 -                      container_of(aux, struct esr_context, head);
 +      if (err == 0 && user->esr_offset) {
 +              struct esr_context __user *esr_ctx =
 +                      apply_user_offset(user, user->esr_offset);
 +
                __put_user_error(ESR_MAGIC, &esr_ctx->head.magic, err);
                __put_user_error(sizeof(*esr_ctx), &esr_ctx->head.size, err);
                __put_user_error(current->thread.fault_code, &esr_ctx->esr, err);
 -              aux += sizeof(*esr_ctx);
 +      }
 +
 +      if (err == 0 && user->extra_offset) {
 +              char __user *sfp = (char __user *)user->sigframe;
 +              char __user *userp =
 +                      apply_user_offset(user, user->extra_offset);
 +
 +              struct extra_context __user *extra;
 +              struct _aarch64_ctx __user *end;
 +              u64 extra_datap;
 +              u32 extra_size;
 +
 +              extra = (struct extra_context __user *)userp;
 +              userp += EXTRA_CONTEXT_SIZE;
 +
 +              end = (struct _aarch64_ctx __user *)userp;
 +              userp += TERMINATOR_SIZE;
 +
 +              /*
 +               * extra_datap is just written to the signal frame.
 +               * The value gets cast back to a void __user *
 +               * during sigreturn.
 +               */
 +              extra_datap = (__force u64)userp;
 +              extra_size = sfp + round_up(user->size, 16) - userp;
 +
 +              __put_user_error(EXTRA_MAGIC, &extra->head.magic, err);
 +              __put_user_error(EXTRA_CONTEXT_SIZE, &extra->head.size, err);
 +              __put_user_error(extra_datap, &extra->datap, err);
 +              __put_user_error(extra_size, &extra->size, err);
 +
 +              /* Add the terminator */
 +              __put_user_error(0, &end->magic, err);
 +              __put_user_error(0, &end->size, err);
        }
  
        /* set the "end" magic */
 -      end = aux;
 -      __put_user_error(0, &end->magic, err);
 -      __put_user_error(0, &end->size, err);
 +      if (err == 0) {
 +              struct _aarch64_ctx __user *end =
 +                      apply_user_offset(user, user->end_offset);
 +
 +              __put_user_error(0, &end->magic, err);
 +              __put_user_error(0, &end->size, err);
 +      }
  
        return err;
  }
  
 -static struct rt_sigframe __user *get_sigframe(struct ksignal *ksig,
 -                                             struct pt_regs *regs)
 +static int get_sigframe(struct rt_sigframe_user_layout *user,
 +                       struct ksignal *ksig, struct pt_regs *regs)
  {
        unsigned long sp, sp_top;
 -      struct rt_sigframe __user *frame;
 +      int err;
 +
 +      init_user_layout(user);
 +      err = setup_sigframe_layout(user);
 +      if (err)
 +              return err;
  
        sp = sp_top = sigsp(regs->sp, ksig);
  
 -      sp = (sp - sizeof(struct rt_sigframe)) & ~15;
 -      frame = (struct rt_sigframe __user *)sp;
 +      sp = round_down(sp - sizeof(struct frame_record), 16);
 +      user->next_frame = (struct frame_record __user *)sp;
 +
 +      sp = round_down(sp, 16) - sigframe_size(user);
 +      user->sigframe = (struct rt_sigframe __user *)sp;
  
        /*
         * Check that we can actually write to the signal frame.
         */
 -      if (!access_ok(VERIFY_WRITE, frame, sp_top - sp))
 -              frame = NULL;
 +      if (!access_ok(VERIFY_WRITE, user->sigframe, sp_top - sp))
 +              return -EFAULT;
  
 -      return frame;
 +      return 0;
  }
  
  static void setup_return(struct pt_regs *regs, struct k_sigaction *ka,
 -                       void __user *frame, int usig)
 +                       struct rt_sigframe_user_layout *user, int usig)
  {
        __sigrestore_t sigtramp;
  
        regs->regs[0] = usig;
 -      regs->sp = (unsigned long)frame;
 -      regs->regs[29] = regs->sp + offsetof(struct rt_sigframe, fp);
 +      regs->sp = (unsigned long)user->sigframe;
 +      regs->regs[29] = (unsigned long)&user->next_frame->fp;
        regs->pc = (unsigned long)ka->sa.sa_handler;
  
        if (ka->sa.sa_flags & SA_RESTORER)
  static int setup_rt_frame(int usig, struct ksignal *ksig, sigset_t *set,
                          struct pt_regs *regs)
  {
 +      struct rt_sigframe_user_layout user;
        struct rt_sigframe __user *frame;
        int err = 0;
  
 -      frame = get_sigframe(ksig, regs);
 -      if (!frame)
 +      if (get_sigframe(&user, ksig, regs))
                return 1;
  
 +      frame = user.sigframe;
 +
        __put_user_error(0, &frame->uc.uc_flags, err);
        __put_user_error(NULL, &frame->uc.uc_link, err);
  
        err |= __save_altstack(&frame->uc.uc_stack, regs->sp);
 -      err |= setup_sigframe(frame, regs, set);
 +      err |= setup_sigframe(&user, regs, set);
        if (err == 0) {
 -              setup_return(regs, &ksig->ka, frame, usig);
 +              setup_return(regs, &ksig->ka, &user, usig);
                if (ksig->ka.sa.sa_flags & SA_SIGINFO) {
                        err |= copy_siginfo_to_user(&frame->info, &ksig->info);
                        regs->regs[1] = (unsigned long)&frame->info;
@@@ -749,6 -409,10 +750,10 @@@ asmlinkage void do_notify_resume(struc
         * Update the trace code with the current status.
         */
        trace_hardirqs_off();
+       /* Check valid user FS if needed */
+       addr_limit_user_check();
        do {
                if (thread_flags & _TIF_NEED_RESCHED) {
                        schedule();
index 30269dafec47916e0f46866fa9cb3c19e93f9828,11433f9018e22c3d5d1e53815b12324c8940e877..184eb9894dae3f2cca10bfe1813a348614830305
  
  #define get_ds()      (KERNEL_DS)
  #define get_fs()      (current->thread.addr_limit)
- #define set_fs(x)     (current->thread.addr_limit = (x))
+ static inline void set_fs(mm_segment_t fs)
+ {
+       current->thread.addr_limit = fs;
+       /* On user-mode return, check fs is correct */
+       set_thread_flag(TIF_FSCHECK);
+ }
  
  #define segment_eq(a, b)      ((a).seg == (b).seg)
  
@@@ -535,6 -540,9 +540,6 @@@ struct __large_struct { unsigned long b
  #define __put_user(x, ptr)                                            \
        __put_user_nocheck((__typeof__(*(ptr)))(x), (ptr), sizeof(*(ptr)))
  
 -#define __get_user_unaligned __get_user
 -#define __put_user_unaligned __put_user
 -
  /*
   * {get|put}_user_try and catch
   *
@@@ -562,6 -570,7 +567,6 @@@ copy_from_user_nmi(void *to, const voi
  extern __must_check long
  strncpy_from_user(char *dst, const char __user *src, long count);
  
 -extern __must_check long strlen_user(const char __user *str);
  extern __must_check long strnlen_user(const char __user *str, long n);
  
  unsigned long __must_check clear_user(void __user *mem, unsigned long len);
diff --combined include/linux/syscalls.h
index 138c945358646ffbe975cb51f8e8fecba32d4b3e,ac0cf6fb25d6359c6a6a2eef271d932b5ff7ffa6..d4dfac878fabe3cd5f2133117f3fa22b8409c623
@@@ -100,12 -100,11 +100,12 @@@ union bpf_attr
  #define __MAP(n,...) __MAP##n(__VA_ARGS__)
  
  #define __SC_DECL(t, a)       t a
 -#define __TYPE_IS_L(t)        (__same_type((t)0, 0L))
 -#define __TYPE_IS_UL(t)       (__same_type((t)0, 0UL))
 -#define __TYPE_IS_LL(t) (__same_type((t)0, 0LL) || __same_type((t)0, 0ULL))
 +#define __TYPE_AS(t, v)       __same_type((__force t)0, v)
 +#define __TYPE_IS_L(t)        (__TYPE_AS(t, 0L))
 +#define __TYPE_IS_UL(t)       (__TYPE_AS(t, 0UL))
 +#define __TYPE_IS_LL(t) (__TYPE_AS(t, 0LL) || __TYPE_AS(t, 0ULL))
  #define __SC_LONG(t, a) __typeof(__builtin_choose_expr(__TYPE_IS_LL(t), 0LL, 0L)) a
 -#define __SC_CAST(t, a)       (t) a
 +#define __SC_CAST(t, a)       (__force t) a
  #define __SC_ARGS(t, a)       a
  #define __SC_TEST(t, a) (void)BUILD_BUG_ON_ZERO(!__TYPE_IS_LL(t) && sizeof(t) > sizeof(long))
  
@@@ -207,6 -206,22 +207,22 @@@ extern struct trace_event_functions exi
        }                                                               \
        static inline long SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__))
  
+ #ifdef TIF_FSCHECK
+ /*
+  * Called before coming back to user-mode. Returning to user-mode with an
+  * address limit different than USER_DS can allow to overwrite kernel memory.
+  */
+ static inline void addr_limit_user_check(void)
+ {
+       if (!test_thread_flag(TIF_FSCHECK))
+               return;
+       BUG_ON(!segment_eq(get_fs(), USER_DS));
+       clear_thread_flag(TIF_FSCHECK);
+ }
+ #endif
  asmlinkage long sys32_quotactl(unsigned int cmd, const char __user *special,
                               qid_t id, void __user *addr);
  asmlinkage long sys_time(time_t __user *tloc);
@@@ -579,12 -594,12 +595,12 @@@ asmlinkage long sys_preadv(unsigned lon
                           unsigned long vlen, unsigned long pos_l, unsigned long pos_h);
  asmlinkage long sys_preadv2(unsigned long fd, const struct iovec __user *vec,
                            unsigned long vlen, unsigned long pos_l, unsigned long pos_h,
 -                          int flags);
 +                          rwf_t flags);
  asmlinkage long sys_pwritev(unsigned long fd, const struct iovec __user *vec,
                            unsigned long vlen, unsigned long pos_l, unsigned long pos_h);
  asmlinkage long sys_pwritev2(unsigned long fd, const struct iovec __user *vec,
                            unsigned long vlen, unsigned long pos_l, unsigned long pos_h,
 -                          int flags);
 +                          rwf_t flags);
  asmlinkage long sys_getcwd(char __user *buf, unsigned long size);
  asmlinkage long sys_mkdir(const char __user *pathname, umode_t mode);
  asmlinkage long sys_chdir(const char __user *filename);
@@@ -651,7 -666,7 +667,7 @@@ asmlinkage long sys_olduname(struct old
  
  asmlinkage long sys_getrlimit(unsigned int resource,
                                struct rlimit __user *rlim);
 -#if defined(COMPAT_RLIM_OLD_INFINITY) || !(defined(CONFIG_IA64))
 +#ifdef __ARCH_WANT_SYS_OLD_GETRLIMIT
  asmlinkage long sys_old_getrlimit(unsigned int resource, struct rlimit __user *rlim);
  #endif
  asmlinkage long sys_setrlimit(unsigned int resource,