Merge branch 'for-v5.13-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/ebieder...
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 21 May 2021 16:12:52 +0000 (06:12 -1000)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 21 May 2021 16:12:52 +0000 (06:12 -1000)
Pull siginfo fix from Eric Biederman:
 "During the merge window an issue with si_perf and the siginfo ABI came
  up. The alpha and sparc siginfo structure layout had changed with the
  addition of SIGTRAP TRAP_PERF and the new field si_perf.

  The reason only alpha and sparc were affected is that they are the
  only architectures that use si_trapno.

  Looking deeper it was discovered that si_trapno is used for only a few
  select signals on alpha and sparc, and that none of the other
  _sigfault fields past si_addr are used at all. Which means technically
  no regression on alpha and sparc.

  While the alignment concerns might be dismissed the abuse of si_errno
  by SIGTRAP TRAP_PERF does have the potential to cause regressions in
  existing userspace.

  While we still have time before userspace starts using and depending
  on the new definition siginfo for SIGTRAP TRAP_PERF this set of
  changes cleans up siginfo_t.

   - The si_trapno field is demoted from magic alpha and sparc status
     and made an ordinary union member of the _sigfault member of
     siginfo_t. Without moving it of course.

   - si_perf is replaced with si_perf_data and si_perf_type ending the
     abuse of si_errno.

   - Unnecessary additions to signalfd_siginfo are removed"

* 'for-v5.13-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace:
  signalfd: Remove SIL_PERF_EVENT fields from signalfd_siginfo
  signal: Deliver all of the siginfo perf data in _perf
  signal: Factor force_sig_perf out of perf_sigtrap
  signal: Implement SIL_FAULT_TRAPNO
  siginfo: Move si_trapno inside the union inside _si_fault

1  2 
include/linux/compat.h
include/linux/signal.h
include/uapi/linux/perf_event.h
kernel/events/core.c
kernel/signal.c

diff --combined include/linux/compat.h
@@@ -75,6 -75,7 +75,6 @@@
        __diag_push();                                                          \
        __diag_ignore(GCC, 8, "-Wattribute-alias",                              \
                      "Type aliasing is used to sanitize syscall arguments");\
 -      asmlinkage long compat_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));       \
        asmlinkage long compat_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__))        \
                __attribute__((alias(__stringify(__se_compat_sys##name))));     \
        ALLOW_ERROR_INJECTION(compat_sys##name, ERRNO);                         \
@@@ -213,12 -214,11 +213,11 @@@ typedef struct compat_siginfo 
                /* SIGILL, SIGFPE, SIGSEGV, SIGBUS, SIGTRAP, SIGEMT */
                struct {
                        compat_uptr_t _addr;    /* faulting insn/memory ref. */
- #ifdef __ARCH_SI_TRAPNO
-                       int _trapno;    /* TRAP # which caused the signal */
- #endif
  #define __COMPAT_ADDR_BND_PKEY_PAD  (__alignof__(compat_uptr_t) < sizeof(short) ? \
                                     sizeof(short) : __alignof__(compat_uptr_t))
                        union {
+                               /* used on alpha and sparc */
+                               int _trapno;    /* TRAP # which caused the signal */
                                /*
                                 * used when si_code=BUS_MCEERR_AR or
                                 * used when si_code=BUS_MCEERR_AO
                                        u32 _pkey;
                                } _addr_pkey;
                                /* used when si_code=TRAP_PERF */
-                               compat_ulong_t _perf;
+                               struct {
+                                       compat_ulong_t _data;
+                                       u32 _type;
+                               } _perf;
                        };
                } _sigfault;
  
@@@ -466,34 -469,6 +468,34 @@@ put_compat_sigset(compat_sigset_t __use
                unsafe_put_user(__s->sig[0], &__c->sig[0], label);      \
        }                                                               \
  } while (0)
 +
 +#define unsafe_get_compat_sigset(set, compat, label) do {             \
 +      const compat_sigset_t __user *__c = compat;                     \
 +      compat_sigset_word hi, lo;                                      \
 +      sigset_t *__s = set;                                            \
 +                                                                      \
 +      switch (_NSIG_WORDS) {                                          \
 +      case 4:                                                         \
 +              unsafe_get_user(lo, &__c->sig[7], label);               \
 +              unsafe_get_user(hi, &__c->sig[6], label);               \
 +              __s->sig[3] = hi | (((long)lo) << 32);                  \
 +              fallthrough;                                            \
 +      case 3:                                                         \
 +              unsafe_get_user(lo, &__c->sig[5], label);               \
 +              unsafe_get_user(hi, &__c->sig[4], label);               \
 +              __s->sig[2] = hi | (((long)lo) << 32);                  \
 +              fallthrough;                                            \
 +      case 2:                                                         \
 +              unsafe_get_user(lo, &__c->sig[3], label);               \
 +              unsafe_get_user(hi, &__c->sig[2], label);               \
 +              __s->sig[1] = hi | (((long)lo) << 32);                  \
 +              fallthrough;                                            \
 +      case 1:                                                         \
 +              unsafe_get_user(lo, &__c->sig[1], label);               \
 +              unsafe_get_user(hi, &__c->sig[0], label);               \
 +              __s->sig[0] = hi | (((long)lo) << 32);                  \
 +      }                                                               \
 +} while (0)
  #else
  #define unsafe_put_compat_sigset(compat, set, label) do {             \
        compat_sigset_t __user *__c = compat;                           \
                                                                        \
        unsafe_copy_to_user(__c, __s, sizeof(*__c), label);             \
  } while (0)
 +
 +#define unsafe_get_compat_sigset(set, compat, label) do {             \
 +      const compat_sigset_t __user *__c = compat;                     \
 +      sigset_t *__s = set;                                            \
 +                                                                      \
 +      unsafe_copy_from_user(__s, __c, sizeof(*__c), label);           \
 +} while (0)
  #endif
  
  extern int compat_ptrace_request(struct task_struct *child,
diff --combined include/linux/signal.h
@@@ -40,6 -40,7 +40,7 @@@ enum siginfo_layout 
        SIL_TIMER,
        SIL_POLL,
        SIL_FAULT,
+       SIL_FAULT_TRAPNO,
        SIL_FAULT_MCEERR,
        SIL_FAULT_BNDERR,
        SIL_FAULT_PKUERR,
@@@ -266,7 -267,6 +267,7 @@@ static inline void init_sigpending(stru
  }
  
  extern void flush_sigqueue(struct sigpending *queue);
 +extern void exit_task_sigqueue_cache(struct task_struct *tsk);
  
  /* Test if 'sig' is valid signal. Use this instead of testing _NSIG directly */
  static inline int valid_signal(unsigned long sig)
@@@ -464,7 -464,7 +464,7 @@@ struct perf_event_attr 
  
        /*
         * User provided data if sigtrap=1, passed back to user via
-        * siginfo_t::si_perf, e.g. to permit user to identify the event.
+        * siginfo_t::si_perf_data, e.g. to permit user to identify the event.
         */
        __u64   sig_data;
  };
@@@ -1182,15 -1182,10 +1182,15 @@@ enum perf_callchain_context 
  /**
   * PERF_RECORD_AUX::flags bits
   */
 -#define PERF_AUX_FLAG_TRUNCATED               0x01    /* record was truncated to fit */
 -#define PERF_AUX_FLAG_OVERWRITE               0x02    /* snapshot from overwrite mode */
 -#define PERF_AUX_FLAG_PARTIAL         0x04    /* record contains gaps */
 -#define PERF_AUX_FLAG_COLLISION               0x08    /* sample collided with another */
 +#define PERF_AUX_FLAG_TRUNCATED                       0x01    /* record was truncated to fit */
 +#define PERF_AUX_FLAG_OVERWRITE                       0x02    /* snapshot from overwrite mode */
 +#define PERF_AUX_FLAG_PARTIAL                 0x04    /* record contains gaps */
 +#define PERF_AUX_FLAG_COLLISION                       0x08    /* sample collided with another */
 +#define PERF_AUX_FLAG_PMU_FORMAT_TYPE_MASK    0xff00  /* PMU specific trace format type */
 +
 +/* CoreSight PMU AUX buffer formats */
 +#define PERF_AUX_FLAG_CORESIGHT_FORMAT_CORESIGHT      0x0000 /* Default for backward compatibility */
 +#define PERF_AUX_FLAG_CORESIGHT_FORMAT_RAW            0x0100 /* Raw format of the source */
  
  #define PERF_FLAG_FD_NO_GROUP         (1UL << 0)
  #define PERF_FLAG_FD_OUTPUT           (1UL << 1)
diff --combined kernel/events/core.c
@@@ -581,6 -581,11 +581,6 @@@ static u64 perf_event_time(struct perf_
  
  void __weak perf_event_print_debug(void)      { }
  
 -extern __weak const char *perf_pmu_name(void)
 -{
 -      return "pmu";
 -}
 -
  static inline u64 perf_clock(void)
  {
        return local_clock();
@@@ -6389,8 -6394,6 +6389,6 @@@ void perf_event_wakeup(struct perf_even
  
  static void perf_sigtrap(struct perf_event *event)
  {
-       struct kernel_siginfo info;
        /*
         * We'd expect this to only occur if the irq_work is delayed and either
         * ctx->task or current has changed in the meantime. This can be the
        if (current->flags & PF_EXITING)
                return;
  
-       clear_siginfo(&info);
-       info.si_signo = SIGTRAP;
-       info.si_code = TRAP_PERF;
-       info.si_errno = event->attr.type;
-       info.si_perf = event->attr.sig_data;
-       info.si_addr = (void __user *)event->pending_addr;
-       force_sig_info(&info);
+       force_sig_perf((void __user *)event->pending_addr,
+                      event->attr.type, event->attr.sig_data);
  }
  
  static void perf_pending_event_disable(struct perf_event *event)
diff --combined kernel/signal.c
@@@ -408,8 -408,7 +408,8 @@@ void task_join_group_stop(struct task_s
   *   appropriate lock must be held to stop the target task from exiting
   */
  static struct sigqueue *
 -__sigqueue_alloc(int sig, struct task_struct *t, gfp_t flags, int override_rlimit)
 +__sigqueue_alloc(int sig, struct task_struct *t, gfp_t gfp_flags,
 +               int override_rlimit, const unsigned int sigqueue_flags)
  {
        struct sigqueue *q = NULL;
        struct user_struct *user;
        rcu_read_unlock();
  
        if (override_rlimit || likely(sigpending <= task_rlimit(t, RLIMIT_SIGPENDING))) {
 -              q = kmem_cache_alloc(sigqueue_cachep, flags);
 +              /*
 +               * Preallocation does not hold sighand::siglock so it can't
 +               * use the cache. The lockless caching requires that only
 +               * one consumer and only one producer run at a time.
 +               */
 +              q = READ_ONCE(t->sigqueue_cache);
 +              if (!q || sigqueue_flags)
 +                      q = kmem_cache_alloc(sigqueue_cachep, gfp_flags);
 +              else
 +                      WRITE_ONCE(t->sigqueue_cache, NULL);
        } else {
                print_dropped_signal(sig);
        }
                        free_uid(user);
        } else {
                INIT_LIST_HEAD(&q->list);
 -              q->flags = 0;
 +              q->flags = sigqueue_flags;
                q->user = user;
        }
  
        return q;
  }
  
 +void exit_task_sigqueue_cache(struct task_struct *tsk)
 +{
 +      /* Race free because @tsk is mopped up */
 +      struct sigqueue *q = tsk->sigqueue_cache;
 +
 +      if (q) {
 +              tsk->sigqueue_cache = NULL;
 +              /*
 +               * Hand it back to the cache as the task might
 +               * be self reaping which would leak the object.
 +               */
 +               kmem_cache_free(sigqueue_cachep, q);
 +      }
 +}
 +
 +static void sigqueue_cache_or_free(struct sigqueue *q)
 +{
 +      /*
 +       * Cache one sigqueue per task. This pairs with the consumer side
 +       * in __sigqueue_alloc() and needs READ/WRITE_ONCE() to prevent the
 +       * compiler from store tearing and to tell KCSAN that the data race
 +       * is intentional when run without holding current->sighand->siglock,
 +       * which is fine as current obviously cannot run __sigqueue_free()
 +       * concurrently.
 +       */
 +      if (!READ_ONCE(current->sigqueue_cache))
 +              WRITE_ONCE(current->sigqueue_cache, q);
 +      else
 +              kmem_cache_free(sigqueue_cachep, q);
 +}
 +
  static void __sigqueue_free(struct sigqueue *q)
  {
        if (q->flags & SIGQUEUE_PREALLOC)
                return;
        if (atomic_dec_and_test(&q->user->sigpending))
                free_uid(q->user);
 -      kmem_cache_free(sigqueue_cachep, q);
 +      sigqueue_cache_or_free(q);
  }
  
  void flush_sigqueue(struct sigpending *queue)
@@@ -1152,8 -1111,7 +1152,8 @@@ static int __send_signal(int sig, struc
        else
                override_rlimit = 0;
  
 -      q = __sigqueue_alloc(sig, t, GFP_ATOMIC, override_rlimit);
 +      q = __sigqueue_alloc(sig, t, GFP_ATOMIC, override_rlimit, 0);
 +
        if (q) {
                list_add_tail(&q->list, &pending->list);
                switch ((unsigned long) info) {
@@@ -1236,6 -1194,7 +1236,7 @@@ static inline bool has_si_pid_and_uid(s
        case SIL_TIMER:
        case SIL_POLL:
        case SIL_FAULT:
+       case SIL_FAULT_TRAPNO:
        case SIL_FAULT_MCEERR:
        case SIL_FAULT_BNDERR:
        case SIL_FAULT_PKUERR:
@@@ -1804,6 -1763,21 +1805,21 @@@ int force_sig_pkuerr(void __user *addr
  }
  #endif
  
+ int force_sig_perf(void __user *addr, u32 type, u64 sig_data)
+ {
+       struct kernel_siginfo info;
+       clear_siginfo(&info);
+       info.si_signo     = SIGTRAP;
+       info.si_errno     = 0;
+       info.si_code      = TRAP_PERF;
+       info.si_addr      = addr;
+       info.si_perf_data = sig_data;
+       info.si_perf_type = type;
+       return force_sig_info(&info);
+ }
  /* For the crazy architectures that include trap information in
   * the errno field, instead of an actual errno value.
   */
@@@ -1848,7 -1822,12 +1864,7 @@@ EXPORT_SYMBOL(kill_pid)
   */
  struct sigqueue *sigqueue_alloc(void)
  {
 -      struct sigqueue *q = __sigqueue_alloc(-1, current, GFP_KERNEL, 0);
 -
 -      if (q)
 -              q->flags |= SIGQUEUE_PREALLOC;
 -
 -      return q;
 +      return __sigqueue_alloc(-1, current, GFP_KERNEL, 0, SIGQUEUE_PREALLOC);
  }
  
  void sigqueue_free(struct sigqueue *q)
@@@ -2564,6 -2543,7 +2580,7 @@@ static void hide_si_addr_tag_bits(struc
  {
        switch (siginfo_layout(ksig->sig, ksig->info.si_code)) {
        case SIL_FAULT:
+       case SIL_FAULT_TRAPNO:
        case SIL_FAULT_MCEERR:
        case SIL_FAULT_BNDERR:
        case SIL_FAULT_PKUERR:
@@@ -3251,6 -3231,10 +3268,10 @@@ enum siginfo_layout siginfo_layout(unsi
  #endif
                        else if ((sig == SIGTRAP) && (si_code == TRAP_PERF))
                                layout = SIL_PERF_EVENT;
+ #ifdef __ARCH_SI_TRAPNO
+                       else if (layout == SIL_FAULT)
+                               layout = SIL_FAULT_TRAPNO;
+ #endif
                }
                else if (si_code <= NSIGPOLL)
                        layout = SIL_POLL;
@@@ -3354,35 -3338,28 +3375,28 @@@ void copy_siginfo_to_external32(struct 
                break;
        case SIL_FAULT:
                to->si_addr = ptr_to_compat(from->si_addr);
- #ifdef __ARCH_SI_TRAPNO
+               break;
+       case SIL_FAULT_TRAPNO:
+               to->si_addr = ptr_to_compat(from->si_addr);
                to->si_trapno = from->si_trapno;
- #endif
                break;
        case SIL_FAULT_MCEERR:
                to->si_addr = ptr_to_compat(from->si_addr);
- #ifdef __ARCH_SI_TRAPNO
-               to->si_trapno = from->si_trapno;
- #endif
                to->si_addr_lsb = from->si_addr_lsb;
                break;
        case SIL_FAULT_BNDERR:
                to->si_addr = ptr_to_compat(from->si_addr);
- #ifdef __ARCH_SI_TRAPNO
-               to->si_trapno = from->si_trapno;
- #endif
                to->si_lower = ptr_to_compat(from->si_lower);
                to->si_upper = ptr_to_compat(from->si_upper);
                break;
        case SIL_FAULT_PKUERR:
                to->si_addr = ptr_to_compat(from->si_addr);
- #ifdef __ARCH_SI_TRAPNO
-               to->si_trapno = from->si_trapno;
- #endif
                to->si_pkey = from->si_pkey;
                break;
        case SIL_PERF_EVENT:
                to->si_addr = ptr_to_compat(from->si_addr);
-               to->si_perf = from->si_perf;
+               to->si_perf_data = from->si_perf_data;
+               to->si_perf_type = from->si_perf_type;
                break;
        case SIL_CHLD:
                to->si_pid = from->si_pid;
@@@ -3438,35 -3415,28 +3452,28 @@@ static int post_copy_siginfo_from_user3
                break;
        case SIL_FAULT:
                to->si_addr = compat_ptr(from->si_addr);
- #ifdef __ARCH_SI_TRAPNO
+               break;
+       case SIL_FAULT_TRAPNO:
+               to->si_addr = compat_ptr(from->si_addr);
                to->si_trapno = from->si_trapno;
- #endif
                break;
        case SIL_FAULT_MCEERR:
                to->si_addr = compat_ptr(from->si_addr);
- #ifdef __ARCH_SI_TRAPNO
-               to->si_trapno = from->si_trapno;
- #endif
                to->si_addr_lsb = from->si_addr_lsb;
                break;
        case SIL_FAULT_BNDERR:
                to->si_addr = compat_ptr(from->si_addr);
- #ifdef __ARCH_SI_TRAPNO
-               to->si_trapno = from->si_trapno;
- #endif
                to->si_lower = compat_ptr(from->si_lower);
                to->si_upper = compat_ptr(from->si_upper);
                break;
        case SIL_FAULT_PKUERR:
                to->si_addr = compat_ptr(from->si_addr);
- #ifdef __ARCH_SI_TRAPNO
-               to->si_trapno = from->si_trapno;
- #endif
                to->si_pkey = from->si_pkey;
                break;
        case SIL_PERF_EVENT:
                to->si_addr = compat_ptr(from->si_addr);
-               to->si_perf = from->si_perf;
+               to->si_perf_data = from->si_perf_data;
+               to->si_perf_type = from->si_perf_type;
                break;
        case SIL_CHLD:
                to->si_pid    = from->si_pid;
@@@ -4644,11 -4614,13 +4651,13 @@@ static inline void siginfo_buildtime_ch
  
        /* sigfault */
        CHECK_OFFSET(si_addr);
+       CHECK_OFFSET(si_trapno);
        CHECK_OFFSET(si_addr_lsb);
        CHECK_OFFSET(si_lower);
        CHECK_OFFSET(si_upper);
        CHECK_OFFSET(si_pkey);
-       CHECK_OFFSET(si_perf);
+       CHECK_OFFSET(si_perf_data);
+       CHECK_OFFSET(si_perf_type);
  
        /* sigpoll */
        CHECK_OFFSET(si_band);