]> bbs.cooldavid.org Git - net-next-2.6.git/commitdiff
Merge branch 'core-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
authorLinus Torvalds <torvalds@linux-foundation.org>
Sat, 19 Dec 2009 17:47:34 +0000 (09:47 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 19 Dec 2009 17:47:34 +0000 (09:47 -0800)
* 'core-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip:
  sys: Fix missing rcu protection for __task_cred() access
  signals: Fix more rcu assumptions
  signal: Fix racy access to __task_cred in kill_pid_info_as_uid()

1  2 
kernel/signal.c
kernel/sys.c

diff --combined kernel/signal.c
index 1814e68e4de38297d0fcf1a8d2c1caa6056354d3,f67545f9394c6e034fbc6e3a181aa88d0661d3c9..d09692b4037614cf8a596ec229002018ebee9aac
@@@ -218,13 -218,13 +218,13 @@@ __sigqueue_alloc(int sig, struct task_s
        struct user_struct *user;
  
        /*
-        * We won't get problems with the target's UID changing under us
-        * because changing it requires RCU be used, and if t != current, the
-        * caller must be holding the RCU readlock (by way of a spinlock) and
-        * we use RCU protection here
+        * Protect access to @t credentials. This can go away when all
+        * callers hold rcu read lock.
         */
+       rcu_read_lock();
        user = get_uid(__task_cred(t)->user);
        atomic_inc(&user->sigpending);
+       rcu_read_unlock();
  
        if (override_rlimit ||
            atomic_read(&user->sigpending) <=
@@@ -423,7 -423,7 +423,7 @@@ still_pending
                 */
                info->si_signo = sig;
                info->si_errno = 0;
 -              info->si_code = 0;
 +              info->si_code = SI_USER;
                info->si_pid = 0;
                info->si_uid = 0;
        }
@@@ -607,17 -607,6 +607,17 @@@ static int rm_from_queue(unsigned long 
        return 1;
  }
  
 +static inline int is_si_special(const struct siginfo *info)
 +{
 +      return info <= SEND_SIG_FORCED;
 +}
 +
 +static inline bool si_fromuser(const struct siginfo *info)
 +{
 +      return info == SEND_SIG_NOINFO ||
 +              (!is_si_special(info) && SI_FROMUSER(info));
 +}
 +
  /*
   * Bad permissions for sending the signal
   * - the caller must hold at least the RCU read lock
@@@ -632,7 -621,7 +632,7 @@@ static int check_kill_permission(int si
        if (!valid_signal(sig))
                return -EINVAL;
  
 -      if (info != SEND_SIG_NOINFO && (is_si_special(info) || SI_FROMKERNEL(info)))
 +      if (!si_fromuser(info))
                return 0;
  
        error = audit_signal_info(sig, t); /* Let audit system see the signal */
@@@ -960,8 -949,9 +960,8 @@@ static int send_signal(int sig, struct 
        int from_ancestor_ns = 0;
  
  #ifdef CONFIG_PID_NS
 -      if (!is_si_special(info) && SI_FROMUSER(info) &&
 -                      task_pid_nr_ns(current, task_active_pid_ns(t)) <= 0)
 -              from_ancestor_ns = 1;
 +      from_ancestor_ns = si_fromuser(info) &&
 +                         !task_pid_nr_ns(current, task_active_pid_ns(t));
  #endif
  
        return __send_signal(sig, info, t, group, from_ancestor_ns);
@@@ -1062,6 -1052,12 +1062,6 @@@ force_sig_info(int sig, struct siginfo 
        return ret;
  }
  
 -void
 -force_sig_specific(int sig, struct task_struct *t)
 -{
 -      force_sig_info(sig, SEND_SIG_FORCED, t);
 -}
 -
  /*
   * Nuke all other threads in the group.
   */
@@@ -1179,18 -1175,20 +1179,19 @@@ int kill_pid_info_as_uid(int sig, struc
        int ret = -EINVAL;
        struct task_struct *p;
        const struct cred *pcred;
+       unsigned long flags;
  
        if (!valid_signal(sig))
                return ret;
  
-       read_lock(&tasklist_lock);
+       rcu_read_lock();
        p = pid_task(pid, PIDTYPE_PID);
        if (!p) {
                ret = -ESRCH;
                goto out_unlock;
        }
        pcred = __task_cred(p);
 -      if ((info == SEND_SIG_NOINFO ||
 -           (!is_si_special(info) && SI_FROMUSER(info))) &&
 +      if (si_fromuser(info) &&
            euid != pcred->suid && euid != pcred->uid &&
            uid  != pcred->suid && uid  != pcred->uid) {
                ret = -EPERM;
        ret = security_task_kill(p, info, sig, secid);
        if (ret)
                goto out_unlock;
-       if (sig && p->sighand) {
-               unsigned long flags;
-               spin_lock_irqsave(&p->sighand->siglock, flags);
-               ret = __send_signal(sig, info, p, 1, 0);
-               spin_unlock_irqrestore(&p->sighand->siglock, flags);
+       if (sig) {
+               if (lock_task_sighand(p, &flags)) {
+                       ret = __send_signal(sig, info, p, 1, 0);
+                       unlock_task_sighand(p, &flags);
+               } else
+                       ret = -ESRCH;
        }
  out_unlock:
-       read_unlock(&tasklist_lock);
+       rcu_read_unlock();
        return ret;
  }
  EXPORT_SYMBOL_GPL(kill_pid_info_as_uid);
@@@ -1840,6 -1840,11 +1843,6 @@@ relock
  
        for (;;) {
                struct k_sigaction *ka;
 -
 -              if (unlikely(signal->group_stop_count > 0) &&
 -                  do_signal_stop(0))
 -                      goto relock;
 -
                /*
                 * Tracing can induce an artifical signal and choose sigaction.
                 * The return value in @signr determines the default action,
                if (unlikely(signr != 0))
                        ka = return_ka;
                else {
 +                      if (unlikely(signal->group_stop_count > 0) &&
 +                          do_signal_stop(0))
 +                              goto relock;
 +
                        signr = dequeue_signal(current, &current->blocked,
                                               info);
  
diff --combined kernel/sys.c
index 20ccfb5da6af1bfe9d1eb784b601397a932dff66,bc1dc61c31edfba9d7e38cfac6cdc553c01715e4..26a6b73a6b85bbc2a4ee1e1392d3aa86d1e9a850
@@@ -8,6 -8,7 +8,6 @@@
  #include <linux/mm.h>
  #include <linux/utsname.h>
  #include <linux/mman.h>
 -#include <linux/smp_lock.h>
  #include <linux/notifier.h>
  #include <linux/reboot.h>
  #include <linux/prctl.h>
@@@ -162,6 -163,7 +162,7 @@@ SYSCALL_DEFINE3(setpriority, int, which
        if (niceval > 19)
                niceval = 19;
  
+       rcu_read_lock();
        read_lock(&tasklist_lock);
        switch (which) {
                case PRIO_PROCESS:
                                 !(user = find_user(who)))
                                goto out_unlock;        /* No processes for this user */
  
 -                      do_each_thread(g, p)
 +                      do_each_thread(g, p) {
                                if (__task_cred(p)->uid == who)
                                        error = set_one_prio(p, niceval, error);
 -                      while_each_thread(g, p);
 +                      while_each_thread(g, p);
                        if (who != cred->uid)
                                free_uid(user);         /* For find_user() */
                        break;
        }
  out_unlock:
        read_unlock(&tasklist_lock);
+       rcu_read_unlock();
  out:
        return error;
  }
@@@ -252,13 -255,13 +254,13 @@@ SYSCALL_DEFINE2(getpriority, int, which
                                 !(user = find_user(who)))
                                goto out_unlock;        /* No processes for this user */
  
 -                      do_each_thread(g, p)
 +                      do_each_thread(g, p) {
                                if (__task_cred(p)->uid == who) {
                                        niceval = 20 - task_nice(p);
                                        if (niceval > retval)
                                                retval = niceval;
                                }
 -                      while_each_thread(g, p);
 +                      while_each_thread(g, p);
                        if (who != cred->uid)
                                free_uid(user);         /* for find_user() */
                        break;
@@@ -348,9 -351,6 +350,9 @@@ void kernel_power_off(void
        machine_power_off();
  }
  EXPORT_SYMBOL_GPL(kernel_power_off);
 +
 +static DEFINE_MUTEX(reboot_mutex);
 +
  /*
   * Reboot system call: for obvious reasons only root may call it,
   * and even root needs to set up some magic numbers in the registers
@@@ -383,7 -383,7 +385,7 @@@ SYSCALL_DEFINE4(reboot, int, magic1, in
        if ((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off)
                cmd = LINUX_REBOOT_CMD_HALT;
  
 -      lock_kernel();
 +      mutex_lock(&reboot_mutex);
        switch (cmd) {
        case LINUX_REBOOT_CMD_RESTART:
                kernel_restart(NULL);
  
        case LINUX_REBOOT_CMD_HALT:
                kernel_halt();
 -              unlock_kernel();
                do_exit(0);
                panic("cannot halt");
  
        case LINUX_REBOOT_CMD_POWER_OFF:
                kernel_power_off();
 -              unlock_kernel();
                do_exit(0);
                break;
  
        case LINUX_REBOOT_CMD_RESTART2:
                if (strncpy_from_user(&buffer[0], arg, sizeof(buffer) - 1) < 0) {
 -                      unlock_kernel();
 -                      return -EFAULT;
 +                      ret = -EFAULT;
 +                      break;
                }
                buffer[sizeof(buffer) - 1] = '\0';
  
                ret = -EINVAL;
                break;
        }
 -      unlock_kernel();
 +      mutex_unlock(&reboot_mutex);
        return ret;
  }