]> Git Repo - J-linux.git/commitdiff
Merge branch 'ucount-fixes-for-v5.15' of git://git.kernel.org/pub/scm/linux/kernel...
authorLinus Torvalds <[email protected]>
Fri, 22 Oct 2021 03:27:17 +0000 (17:27 -1000)
committerLinus Torvalds <[email protected]>
Fri, 22 Oct 2021 03:27:17 +0000 (17:27 -1000)
Pull ucounts fixes from Eric Biederman:
 "There has been one very hard to track down bug in the ucount code that
  we have been tracking since roughly v5.14 was released. Alex managed
  to find a reliable reproducer a few days ago and then I was able to
  instrument the code and figure out what the issue was.

  It turns out the sigqueue_alloc single atomic operation optimization
  did not play nicely with ucounts multiple level rlimits. It turned out
  that either sigqueue_alloc or sigqueue_free could be operating on
  multiple levels and trigger the conditions for the optimization on
  more than one level at the same time.

  To deal with that situation I have introduced inc_rlimit_get_ucounts
  and dec_rlimit_put_ucounts that just focuses on the optimization and
  the rlimit and ucount changes.

  While looking into the big bug I found I couple of other little issues
  so I am including those fixes here as well.

  When I have time I would very much like to dig into process ownership
  of the shared signal queue and see if we could pick a single owner for
  the entire queue so that all of the rlimits can count to that owner.
  That should entirely remove the need to call get_ucounts and
  put_ucounts in sigqueue_alloc and sigqueue_free. It is difficult
  because Linux unlike POSIX supports setuid that works on a single
  thread"

* 'ucount-fixes-for-v5.15' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace:
  ucounts: Move get_ucounts from cred_alloc_blank to key_change_session_keyring
  ucounts: Proper error handling in set_cred_ucounts
  ucounts: Pair inc_rlimit_ucounts with dec_rlimit_ucoutns in commit_creds
  ucounts: Fix signal ucount refcounting

1  2 
kernel/signal.c

diff --combined kernel/signal.c
index 952741f6d0f9f24f2364c8d67b8b0bee94c228bd,13d2505a14a0e9274f1a76852726512ca6df2362..487bf4f5dadf476171c081fd8938a88d22993423
@@@ -54,7 -54,6 +54,7 @@@
  #include <asm/unistd.h>
  #include <asm/siginfo.h>
  #include <asm/cacheflush.h>
 +#include <asm/syscall.h>      /* for syscall_get_* */
  
  /*
   * SLAB caches for signal bits.
@@@ -426,22 -425,10 +426,10 @@@ __sigqueue_alloc(int sig, struct task_s
         */
        rcu_read_lock();
        ucounts = task_ucounts(t);
-       sigpending = inc_rlimit_ucounts(ucounts, UCOUNT_RLIMIT_SIGPENDING, 1);
-       switch (sigpending) {
-       case 1:
-               if (likely(get_ucounts(ucounts)))
-                       break;
-               fallthrough;
-       case LONG_MAX:
-               /*
-                * we need to decrease the ucount in the userns tree on any
-                * failure to avoid counts leaking.
-                */
-               dec_rlimit_ucounts(ucounts, UCOUNT_RLIMIT_SIGPENDING, 1);
-               rcu_read_unlock();
-               return NULL;
-       }
+       sigpending = inc_rlimit_get_ucounts(ucounts, UCOUNT_RLIMIT_SIGPENDING);
        rcu_read_unlock();
+       if (!sigpending)
+               return NULL;
  
        if (override_rlimit || likely(sigpending <= task_rlimit(t, RLIMIT_SIGPENDING))) {
                q = kmem_cache_alloc(sigqueue_cachep, gfp_flags);
        }
  
        if (unlikely(q == NULL)) {
-               if (dec_rlimit_ucounts(ucounts, UCOUNT_RLIMIT_SIGPENDING, 1))
-                       put_ucounts(ucounts);
+               dec_rlimit_put_ucounts(ucounts, UCOUNT_RLIMIT_SIGPENDING);
        } else {
                INIT_LIST_HEAD(&q->list);
                q->flags = sigqueue_flags;
@@@ -464,8 -450,8 +451,8 @@@ static void __sigqueue_free(struct sigq
  {
        if (q->flags & SIGQUEUE_PREALLOC)
                return;
-       if (q->ucounts && dec_rlimit_ucounts(q->ucounts, UCOUNT_RLIMIT_SIGPENDING, 1)) {
-               put_ucounts(q->ucounts);
+       if (q->ucounts) {
+               dec_rlimit_put_ucounts(q->ucounts, UCOUNT_RLIMIT_SIGPENDING);
                q->ucounts = NULL;
        }
        kmem_cache_free(sigqueue_cachep, q);
@@@ -1214,7 -1200,7 +1201,7 @@@ static inline bool has_si_pid_and_uid(s
        case SIL_FAULT_MCEERR:
        case SIL_FAULT_BNDERR:
        case SIL_FAULT_PKUERR:
 -      case SIL_PERF_EVENT:
 +      case SIL_FAULT_PERF_EVENT:
        case SIL_SYS:
                ret = false;
                break;
@@@ -1323,7 -1309,7 +1310,7 @@@ int do_send_sig_info(int sig, struct ke
   * that is why we also clear SIGNAL_UNKILLABLE.
   */
  static int
 -force_sig_info_to_task(struct kernel_siginfo *info, struct task_struct *t)
 +force_sig_info_to_task(struct kernel_siginfo *info, struct task_struct *t, bool sigdfl)
  {
        unsigned long int flags;
        int ret, blocked, ignored;
        action = &t->sighand->action[sig-1];
        ignored = action->sa.sa_handler == SIG_IGN;
        blocked = sigismember(&t->blocked, sig);
 -      if (blocked || ignored) {
 +      if (blocked || ignored || sigdfl) {
                action->sa.sa_handler = SIG_DFL;
                if (blocked) {
                        sigdelset(&t->blocked, sig);
  
  int force_sig_info(struct kernel_siginfo *info)
  {
 -      return force_sig_info_to_task(info, current);
 +      return force_sig_info_to_task(info, current, false);
  }
  
  /*
@@@ -1414,21 -1400,6 +1401,21 @@@ struct sighand_struct *__lock_task_sigh
        return sighand;
  }
  
 +#ifdef CONFIG_LOCKDEP
 +void lockdep_assert_task_sighand_held(struct task_struct *task)
 +{
 +      struct sighand_struct *sighand;
 +
 +      rcu_read_lock();
 +      sighand = rcu_dereference(task->sighand);
 +      if (sighand)
 +              lockdep_assert_held(&sighand->siglock);
 +      else
 +              WARN_ON_ONCE(1);
 +      rcu_read_unlock();
 +}
 +#endif
 +
  /*
   * send signal info to all the members of a group
   */
@@@ -1682,6 -1653,7 +1669,6 @@@ void force_sigsegv(int sig
  }
  
  int force_sig_fault_to_task(int sig, int code, void __user *addr
 -      ___ARCH_SI_TRAPNO(int trapno)
        ___ARCH_SI_IA64(int imm, unsigned int flags, unsigned long isr)
        , struct task_struct *t)
  {
        info.si_errno = 0;
        info.si_code  = code;
        info.si_addr  = addr;
 -#ifdef __ARCH_SI_TRAPNO
 -      info.si_trapno = trapno;
 -#endif
  #ifdef __ia64__
        info.si_imm = imm;
        info.si_flags = flags;
        info.si_isr = isr;
  #endif
 -      return force_sig_info_to_task(&info, t);
 +      return force_sig_info_to_task(&info, t, false);
  }
  
  int force_sig_fault(int sig, int code, void __user *addr
 -      ___ARCH_SI_TRAPNO(int trapno)
        ___ARCH_SI_IA64(int imm, unsigned int flags, unsigned long isr))
  {
        return force_sig_fault_to_task(sig, code, addr
 -                                     ___ARCH_SI_TRAPNO(trapno)
                                       ___ARCH_SI_IA64(imm, flags, isr), current);
  }
  
  int send_sig_fault(int sig, int code, void __user *addr
 -      ___ARCH_SI_TRAPNO(int trapno)
        ___ARCH_SI_IA64(int imm, unsigned int flags, unsigned long isr)
        , struct task_struct *t)
  {
        info.si_errno = 0;
        info.si_code  = code;
        info.si_addr  = addr;
 -#ifdef __ARCH_SI_TRAPNO
 -      info.si_trapno = trapno;
 -#endif
  #ifdef __ia64__
        info.si_imm = imm;
        info.si_flags = flags;
@@@ -1799,27 -1780,6 +1786,27 @@@ int force_sig_perf(void __user *addr, u
        return force_sig_info(&info);
  }
  
 +/**
 + * force_sig_seccomp - signals the task to allow in-process syscall emulation
 + * @syscall: syscall number to send to userland
 + * @reason: filter-supplied reason code to send to userland (via si_errno)
 + *
 + * Forces a SIGSYS with a code of SYS_SECCOMP and related sigsys info.
 + */
 +int force_sig_seccomp(int syscall, int reason, bool force_coredump)
 +{
 +      struct kernel_siginfo info;
 +
 +      clear_siginfo(&info);
 +      info.si_signo = SIGSYS;
 +      info.si_code = SYS_SECCOMP;
 +      info.si_call_addr = (void __user *)KSTK_EIP(current);
 +      info.si_errno = reason;
 +      info.si_arch = syscall_get_arch(current);
 +      info.si_syscall = syscall;
 +      return force_sig_info_to_task(&info, current, force_coredump);
 +}
 +
  /* For the crazy architectures that include trap information in
   * the errno field, instead of an actual errno value.
   */
@@@ -1835,39 -1795,6 +1822,39 @@@ int force_sig_ptrace_errno_trap(int err
        return force_sig_info(&info);
  }
  
 +/* For the rare architectures that include trap information using
 + * si_trapno.
 + */
 +int force_sig_fault_trapno(int sig, int code, void __user *addr, int trapno)
 +{
 +      struct kernel_siginfo info;
 +
 +      clear_siginfo(&info);
 +      info.si_signo = sig;
 +      info.si_errno = 0;
 +      info.si_code  = code;
 +      info.si_addr  = addr;
 +      info.si_trapno = trapno;
 +      return force_sig_info(&info);
 +}
 +
 +/* For the rare architectures that include trap information using
 + * si_trapno.
 + */
 +int send_sig_fault_trapno(int sig, int code, void __user *addr, int trapno,
 +                        struct task_struct *t)
 +{
 +      struct kernel_siginfo info;
 +
 +      clear_siginfo(&info);
 +      info.si_signo = sig;
 +      info.si_errno = 0;
 +      info.si_code  = code;
 +      info.si_addr  = addr;
 +      info.si_trapno = trapno;
 +      return send_sig_info(info.si_signo, &info, t);
 +}
 +
  int kill_pgrp(struct pid *pid, int sig, int priv)
  {
        int ret;
@@@ -2617,7 -2544,7 +2604,7 @@@ static void hide_si_addr_tag_bits(struc
        case SIL_FAULT_MCEERR:
        case SIL_FAULT_BNDERR:
        case SIL_FAULT_PKUERR:
 -      case SIL_PERF_EVENT:
 +      case SIL_FAULT_PERF_EVENT:
                ksig->info.si_addr = arch_untagged_si_addr(
                        ksig->info.si_addr, ksig->sig, ksig->info.si_code);
                break;
@@@ -3302,14 -3229,11 +3289,14 @@@ enum siginfo_layout siginfo_layout(unsi
                                layout = SIL_FAULT_PKUERR;
  #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_PERF_EVENT;
 +                      else if (IS_ENABLED(CONFIG_SPARC) &&
 +                               (sig == SIGILL) && (si_code == ILL_ILLTRP))
 +                              layout = SIL_FAULT_TRAPNO;
 +                      else if (IS_ENABLED(CONFIG_ALPHA) &&
 +                               ((sig == SIGFPE) ||
 +                                ((sig == SIGTRAP) && (si_code == TRAP_UNK))))
                                layout = SIL_FAULT_TRAPNO;
 -#endif
                }
                else if (si_code <= NSIGPOLL)
                        layout = SIL_POLL;
@@@ -3431,7 -3355,7 +3418,7 @@@ void copy_siginfo_to_external32(struct 
                to->si_addr = ptr_to_compat(from->si_addr);
                to->si_pkey = from->si_pkey;
                break;
 -      case SIL_PERF_EVENT:
 +      case SIL_FAULT_PERF_EVENT:
                to->si_addr = ptr_to_compat(from->si_addr);
                to->si_perf_data = from->si_perf_data;
                to->si_perf_type = from->si_perf_type;
@@@ -3508,7 -3432,7 +3495,7 @@@ static int post_copy_siginfo_from_user3
                to->si_addr = compat_ptr(from->si_addr);
                to->si_pkey = from->si_pkey;
                break;
 -      case SIL_PERF_EVENT:
 +      case SIL_FAULT_PERF_EVENT:
                to->si_addr = compat_ptr(from->si_addr);
                to->si_perf_data = from->si_perf_data;
                to->si_perf_type = from->si_perf_type;
@@@ -4726,7 -4650,7 +4713,7 @@@ void __init signals_init(void
  {
        siginfo_buildtime_checks();
  
 -      sigqueue_cachep = KMEM_CACHE(sigqueue, SLAB_PANIC);
 +      sigqueue_cachep = KMEM_CACHE(sigqueue, SLAB_PANIC | SLAB_ACCOUNT);
  }
  
  #ifdef CONFIG_KGDB_KDB
This page took 0.074362 seconds and 4 git commands to generate.