Merge branch 'timers-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 11 Aug 2008 23:46:11 +0000 (16:46 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 11 Aug 2008 23:46:11 +0000 (16:46 -0700)
* 'timers-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip:
  posix-timers: fix posix_timer_event() vs dequeue_signal() race
  posix-timers: do_schedule_next_timer: fix the setting of ->si_overrun

1  2 
kernel/posix-timers.c
kernel/signal.c

diff --combined kernel/posix-timers.c
index 9a21681aa80f82f0123917725f341a526d484c89,0ffaeb075e35b9ee4c7f14bbe80026d7a23e6c7a..e36d5798cbff427fca02fd8c9a8fb6f615dbd3fe
@@@ -289,21 -289,29 +289,29 @@@ void do_schedule_next_timer(struct sigi
                else
                        schedule_next_timer(timr);
  
-               info->si_overrun = timr->it_overrun_last;
+               info->si_overrun += timr->it_overrun_last;
        }
  
        if (timr)
                unlock_timer(timr, flags);
  }
  
- int posix_timer_event(struct k_itimer *timr,int si_private)
+ int posix_timer_event(struct k_itimer *timr, int si_private)
  {
-       memset(&timr->sigq->info, 0, sizeof(siginfo_t));
+       /*
+        * FIXME: if ->sigq is queued we can race with
+        * dequeue_signal()->do_schedule_next_timer().
+        *
+        * If dequeue_signal() sees the "right" value of
+        * si_sys_private it calls do_schedule_next_timer().
+        * We re-queue ->sigq and drop ->it_lock().
+        * do_schedule_next_timer() locks the timer
+        * and re-schedules it while ->sigq is pending.
+        * Not really bad, but not that we want.
+        */
        timr->sigq->info.si_sys_private = si_private;
-       /* Send signal to the process that owns this timer.*/
  
        timr->sigq->info.si_signo = timr->it_sigev_signo;
-       timr->sigq->info.si_errno = 0;
        timr->sigq->info.si_code = SI_TIMER;
        timr->sigq->info.si_tid = timr->it_id;
        timr->sigq->info.si_value = timr->it_sigev_value;
@@@ -435,6 -443,7 +443,7 @@@ static struct k_itimer * alloc_posix_ti
                kmem_cache_free(posix_timers_cache, tmr);
                tmr = NULL;
        }
+       memset(&tmr->sigq->info, 0, sizeof(siginfo_t));
        return tmr;
  }
  
@@@ -449,6 -458,9 +458,6 @@@ static void release_posix_timer(struct 
                spin_unlock_irqrestore(&idr_lock, flags);
        }
        sigqueue_free(tmr->sigq);
 -      if (unlikely(tmr->it_process) &&
 -          tmr->it_sigev_notify == (SIGEV_SIGNAL|SIGEV_THREAD_ID))
 -              put_task_struct(tmr->it_process);
        kmem_cache_free(posix_timers_cache, tmr);
  }
  
@@@ -853,10 -865,11 +862,10 @@@ retry_delete
         * This keeps any tasks waiting on the spin lock from thinking
         * they got something (see the lock code above).
         */
 -      if (timer->it_process) {
 -              if (timer->it_sigev_notify == (SIGEV_SIGNAL|SIGEV_THREAD_ID))
 -                      put_task_struct(timer->it_process);
 -              timer->it_process = NULL;
 -      }
 +      if (timer->it_sigev_notify == (SIGEV_SIGNAL|SIGEV_THREAD_ID))
 +              put_task_struct(timer->it_process);
 +      timer->it_process = NULL;
 +
        unlock_timer(timer, flags);
        release_posix_timer(timer, IT_ID_SET);
        return 0;
@@@ -881,10 -894,11 +890,10 @@@ retry_delete
         * This keeps any tasks waiting on the spin lock from thinking
         * they got something (see the lock code above).
         */
 -      if (timer->it_process) {
 -              if (timer->it_sigev_notify == (SIGEV_SIGNAL|SIGEV_THREAD_ID))
 -                      put_task_struct(timer->it_process);
 -              timer->it_process = NULL;
 -      }
 +      if (timer->it_sigev_notify == (SIGEV_SIGNAL|SIGEV_THREAD_ID))
 +              put_task_struct(timer->it_process);
 +      timer->it_process = NULL;
 +
        unlock_timer(timer, flags);
        release_posix_timer(timer, IT_ID_SET);
  }
diff --combined kernel/signal.c
index 954f77d7e3bc2368f405fa2ab83aaf58d6280fb3,c5bf0c0df658b69c493ab7de922832e89cc991fa..c539f60c6f41bc2b9e170b93bf66a8bca5e93ce3
@@@ -22,7 -22,6 +22,7 @@@
  #include <linux/ptrace.h>
  #include <linux/signal.h>
  #include <linux/signalfd.h>
 +#include <linux/tracehook.h>
  #include <linux/capability.h>
  #include <linux/freezer.h>
  #include <linux/pid_namespace.h>
  
  static struct kmem_cache *sigqueue_cachep;
  
 -static int __sig_ignored(struct task_struct *t, int sig)
 +static void __user *sig_handler(struct task_struct *t, int sig)
  {
 -      void __user *handler;
 +      return t->sighand->action[sig - 1].sa.sa_handler;
 +}
  
 +static int sig_handler_ignored(void __user *handler, int sig)
 +{
        /* Is it explicitly or implicitly ignored? */
 -
 -      handler = t->sighand->action[sig - 1].sa.sa_handler;
        return handler == SIG_IGN ||
                (handler == SIG_DFL && sig_kernel_ignore(sig));
  }
  
  static int sig_ignored(struct task_struct *t, int sig)
  {
 -      /*
 -       * Tracers always want to know about signals..
 -       */
 -      if (t->ptrace & PT_PTRACED)
 -              return 0;
 +      void __user *handler;
  
        /*
         * Blocked signals are never ignored, since the
        if (sigismember(&t->blocked, sig) || sigismember(&t->real_blocked, sig))
                return 0;
  
 -      return __sig_ignored(t, sig);
 +      handler = sig_handler(t, sig);
 +      if (!sig_handler_ignored(handler, sig))
 +              return 0;
 +
 +      /*
 +       * Tracers may want to know about even ignored signals.
 +       */
 +      return !tracehook_consider_ignored_signal(t, sig, handler);
  }
  
  /*
@@@ -134,9 -129,7 +134,9 @@@ void recalc_sigpending_and_wake(struct 
  
  void recalc_sigpending(void)
  {
 -      if (!recalc_sigpending_tsk(current) && !freezing(current))
 +      if (unlikely(tracehook_force_sigpending()))
 +              set_thread_flag(TIF_SIGPENDING);
 +      else if (!recalc_sigpending_tsk(current) && !freezing(current))
                clear_thread_flag(TIF_SIGPENDING);
  
  }
@@@ -302,12 -295,12 +302,12 @@@ flush_signal_handlers(struct task_struc
  
  int unhandled_signal(struct task_struct *tsk, int sig)
  {
 +      void __user *handler = tsk->sighand->action[sig-1].sa.sa_handler;
        if (is_global_init(tsk))
                return 1;
 -      if (tsk->ptrace & PT_PTRACED)
 +      if (handler != SIG_IGN && handler != SIG_DFL)
                return 0;
 -      return (tsk->sighand->action[sig-1].sa.sa_handler == SIG_IGN) ||
 -              (tsk->sighand->action[sig-1].sa.sa_handler == SIG_DFL);
 +      return !tracehook_consider_fatal_signal(tsk, sig, handler);
  }
  
  
@@@ -345,9 -338,13 +345,9 @@@ unblock_all_signals(void
        spin_unlock_irqrestore(&current->sighand->siglock, flags);
  }
  
 -static int collect_signal(int sig, struct sigpending *list, siginfo_t *info)
 +static void collect_signal(int sig, struct sigpending *list, siginfo_t *info)
  {
        struct sigqueue *q, *first = NULL;
 -      int still_pending = 0;
 -
 -      if (unlikely(!sigismember(&list->signal, sig)))
 -              return 0;
  
        /*
         * Collect the siginfo appropriate to this signal.  Check if
        */
        list_for_each_entry(q, &list->list, list) {
                if (q->info.si_signo == sig) {
 -                      if (first) {
 -                              still_pending = 1;
 -                              break;
 -                      }
 +                      if (first)
 +                              goto still_pending;
                        first = q;
                }
        }
 +
 +      sigdelset(&list->signal, sig);
 +
        if (first) {
 +still_pending:
                list_del_init(&first->list);
                copy_siginfo(info, &first->info);
                __sigqueue_free(first);
 -              if (!still_pending)
 -                      sigdelset(&list->signal, sig);
        } else {
 -
                /* Ok, it wasn't in the queue.  This must be
                   a fast-pathed signal or we must have been
                   out of queue space.  So zero out the info.
                 */
 -              sigdelset(&list->signal, sig);
                info->si_signo = sig;
                info->si_errno = 0;
                info->si_code = 0;
                info->si_pid = 0;
                info->si_uid = 0;
        }
 -      return 1;
  }
  
  static int __dequeue_signal(struct sigpending *pending, sigset_t *mask,
                        }
                }
  
 -              if (!collect_signal(sig, pending, info))
 -                      sig = 0;
 +              collect_signal(sig, pending, info);
        }
  
        return sig;
@@@ -461,7 -462,8 +461,7 @@@ int dequeue_signal(struct task_struct *
                 * is to alert stop-signal processing code when another
                 * processor has come along and cleared the flag.
                 */
 -              if (!(tsk->signal->flags & SIGNAL_GROUP_EXIT))
 -                      tsk->signal->flags |= SIGNAL_STOP_DEQUEUED;
 +              tsk->signal->flags |= SIGNAL_STOP_DEQUEUED;
        }
        if ((info->si_code & __SI_MASK) == __SI_TIMER && info->si_sys_private) {
                /*
@@@ -598,6 -600,9 +598,6 @@@ static int check_kill_permission(int si
        return security_task_kill(t, info, sig, 0);
  }
  
 -/* forward decl */
 -static void do_notify_parent_cldstop(struct task_struct *tsk, int why);
 -
  /*
   * Handle magic process-wide effects of stop/continue signals. Unlike
   * the signal actions, these happen immediately at signal-generation
@@@ -760,8 -765,7 +760,8 @@@ static void complete_signal(int sig, st
        if (sig_fatal(p, sig) &&
            !(signal->flags & (SIGNAL_UNKILLABLE | SIGNAL_GROUP_EXIT)) &&
            !sigismember(&t->real_blocked, sig) &&
 -          (sig == SIGKILL || !(t->ptrace & PT_PTRACED))) {
 +          (sig == SIGKILL ||
 +           !tracehook_consider_fatal_signal(t, sig, SIG_DFL))) {
                /*
                 * This signal will be fatal to the whole group.
                 */
@@@ -1121,7 -1125,7 +1121,7 @@@ EXPORT_SYMBOL_GPL(kill_pid_info_as_uid)
   * is probably wrong.  Should make it like BSD or SYSV.
   */
  
 -static int kill_something_info(int sig, struct siginfo *info, int pid)
 +static int kill_something_info(int sig, struct siginfo *info, pid_t pid)
  {
        int ret;
  
@@@ -1233,6 -1237,17 +1233,6 @@@ int kill_pid(struct pid *pid, int sig, 
  }
  EXPORT_SYMBOL(kill_pid);
  
 -int
 -kill_proc(pid_t pid, int sig, int priv)
 -{
 -      int ret;
 -
 -      rcu_read_lock();
 -      ret = kill_pid_info(sig, __si_special(priv), find_pid(pid));
 -      rcu_read_unlock();
 -      return ret;
 -}
 -
  /*
   * These functions support sending signals using preallocated sigqueue
   * structures.  This is needed "because realtime applications cannot
@@@ -1304,6 -1319,7 +1304,7 @@@ int send_sigqueue(struct sigqueue *q, s
                q->info.si_overrun++;
                goto out;
        }
+       q->info.si_overrun = 0;
  
        signalfd_notify(t, sig);
        pending = group ? &t->signal->shared_pending : &t->pending;
@@@ -1328,11 -1344,9 +1329,11 @@@ static inline void __wake_up_parent(str
  /*
   * Let a parent know about the death of a child.
   * For a stopped/continued status change, use do_notify_parent_cldstop instead.
 + *
 + * Returns -1 if our parent ignored us and so we've switched to
 + * self-reaping, or else @sig.
   */
 -
 -void do_notify_parent(struct task_struct *tsk, int sig)
 +int do_notify_parent(struct task_struct *tsk, int sig)
  {
        struct siginfo info;
        unsigned long flags;
  
        info.si_uid = tsk->uid;
  
 -      /* FIXME: find out whether or not this is supposed to be c*time. */
 -      info.si_utime = cputime_to_jiffies(cputime_add(tsk->utime,
 +      info.si_utime = cputime_to_clock_t(cputime_add(tsk->utime,
                                                       tsk->signal->utime));
 -      info.si_stime = cputime_to_jiffies(cputime_add(tsk->stime,
 +      info.si_stime = cputime_to_clock_t(cputime_add(tsk->stime,
                                                       tsk->signal->stime));
  
        info.si_status = tsk->exit_code & 0x7f;
                 */
                tsk->exit_signal = -1;
                if (psig->action[SIGCHLD-1].sa.sa_handler == SIG_IGN)
 -                      sig = 0;
 +                      sig = -1;
        }
        if (valid_signal(sig) && sig > 0)
                __group_send_sig_info(sig, &info, tsk->parent);
        __wake_up_parent(tsk, tsk->parent);
        spin_unlock_irqrestore(&psig->siglock, flags);
 +
 +      return sig;
  }
  
  static void do_notify_parent_cldstop(struct task_struct *tsk, int why)
  
        info.si_uid = tsk->uid;
  
 -      /* FIXME: find out whether or not this is supposed to be c*time. */
 -      info.si_utime = cputime_to_jiffies(tsk->utime);
 -      info.si_stime = cputime_to_jiffies(tsk->stime);
 +      info.si_utime = cputime_to_clock_t(tsk->utime);
 +      info.si_stime = cputime_to_clock_t(tsk->stime);
  
        info.si_code = why;
        switch (why) {
@@@ -1478,10 -1492,10 +1479,10 @@@ static inline int may_ptrace_stop(void
         * is a deadlock situation, and pointless because our tracer
         * is dead so don't allow us to stop.
         * If SIGKILL was already sent before the caller unlocked
 -       * ->siglock we must see ->core_waiters != 0. Otherwise it
 +       * ->siglock we must see ->core_state != NULL. Otherwise it
         * is safe to enter schedule().
         */
 -      if (unlikely(current->mm->core_waiters) &&
 +      if (unlikely(current->mm->core_state) &&
            unlikely(current->mm == current->parent->mm))
                return 0;
  
   */
  static int sigkill_pending(struct task_struct *tsk)
  {
 -      return ((sigismember(&tsk->pending.signal, SIGKILL) ||
 -               sigismember(&tsk->signal->shared_pending.signal, SIGKILL)) &&
 -              !unlikely(sigismember(&tsk->blocked, SIGKILL)));
 +      return  sigismember(&tsk->pending.signal, SIGKILL) ||
 +              sigismember(&tsk->signal->shared_pending.signal, SIGKILL);
  }
  
  /*
   */
  static void ptrace_stop(int exit_code, int clear_code, siginfo_t *info)
  {
 -      int killed = 0;
 -
        if (arch_ptrace_stop_needed(exit_code, info)) {
                /*
                 * The arch code has something special to do before a
                spin_unlock_irq(&current->sighand->siglock);
                arch_ptrace_stop(exit_code, info);
                spin_lock_irq(&current->sighand->siglock);
 -              killed = sigkill_pending(current);
 +              if (sigkill_pending(current))
 +                      return;
        }
  
        /*
        __set_current_state(TASK_TRACED);
        spin_unlock_irq(&current->sighand->siglock);
        read_lock(&tasklist_lock);
 -      if (!unlikely(killed) && may_ptrace_stop()) {
 +      if (may_ptrace_stop()) {
                do_notify_parent_cldstop(current, CLD_TRAPPED);
                read_unlock(&tasklist_lock);
                schedule();
@@@ -1608,7 -1624,7 +1609,7 @@@ finish_stop(int stop_count
         * a group stop in progress and we are the last to stop,
         * report to the parent.  When ptraced, every thread reports itself.
         */
 -      if (stop_count == 0 || (current->ptrace & PT_PTRACED)) {
 +      if (tracehook_notify_jctl(stop_count == 0, CLD_STOPPED)) {
                read_lock(&tasklist_lock);
                do_notify_parent_cldstop(current, CLD_STOPPED);
                read_unlock(&tasklist_lock);
@@@ -1643,7 -1659,8 +1644,7 @@@ static int do_signal_stop(int signr
        } else {
                struct task_struct *t;
  
 -              if (unlikely((sig->flags & (SIGNAL_STOP_DEQUEUED | SIGNAL_UNKILLABLE))
 -                                       != SIGNAL_STOP_DEQUEUED) ||
 +              if (!likely(sig->flags & SIGNAL_STOP_DEQUEUED) ||
                    unlikely(signal_group_exit(sig)))
                        return 0;
                /*
@@@ -1744,9 -1761,6 +1745,9 @@@ relock
                signal->flags &= ~SIGNAL_CLD_MASK;
                spin_unlock_irq(&sighand->siglock);
  
 +              if (unlikely(!tracehook_notify_jctl(1, why)))
 +                      goto relock;
 +
                read_lock(&tasklist_lock);
                do_notify_parent_cldstop(current->group_leader, why);
                read_unlock(&tasklist_lock);
                    do_signal_stop(0))
                        goto relock;
  
 -              signr = dequeue_signal(current, &current->blocked, info);
 -              if (!signr)
 -                      break; /* will return 0 */
 +              /*
 +               * Tracing can induce an artifical signal and choose sigaction.
 +               * The return value in @signr determines the default action,
 +               * but @info->si_signo is the signal number we will report.
 +               */
 +              signr = tracehook_get_signal(current, regs, info, return_ka);
 +              if (unlikely(signr < 0))
 +                      goto relock;
 +              if (unlikely(signr != 0))
 +                      ka = return_ka;
 +              else {
 +                      signr = dequeue_signal(current, &current->blocked,
 +                                             info);
  
 -              if (signr != SIGKILL) {
 -                      signr = ptrace_signal(signr, info, regs, cookie);
                        if (!signr)
 -                              continue;
 +                              break; /* will return 0 */
 +
 +                      if (signr != SIGKILL) {
 +                              signr = ptrace_signal(signr, info,
 +                                                    regs, cookie);
 +                              if (!signr)
 +                                      continue;
 +                      }
 +
 +                      ka = &sighand->action[signr-1];
                }
  
 -              ka = &sighand->action[signr-1];
                if (ka->sa.sa_handler == SIG_IGN) /* Do nothing.  */
                        continue;
                if (ka->sa.sa_handler != SIG_DFL) {
                                spin_lock_irq(&sighand->siglock);
                        }
  
 -                      if (likely(do_signal_stop(signr))) {
 +                      if (likely(do_signal_stop(info->si_signo))) {
                                /* It released the siglock.  */
                                goto relock;
                        }
  
                if (sig_kernel_coredump(signr)) {
                        if (print_fatal_signals)
 -                              print_fatal_signal(regs, signr);
 +                              print_fatal_signal(regs, info->si_signo);
                        /*
                         * If it was able to dump core, this kills all
                         * other threads in the group and synchronizes with
                         * first and our do_group_exit call below will use
                         * that value and ignore the one we pass it.
                         */
 -                      do_coredump((long)signr, signr, regs);
 +                      do_coredump(info->si_signo, info->si_signo, regs);
                }
  
                /*
                 * Death signals, no core dump.
                 */
 -              do_group_exit(signr);
 +              do_group_exit(info->si_signo);
                /* NOTREACHED */
        }
        spin_unlock_irq(&sighand->siglock);
@@@ -1912,7 -1910,7 +1913,7 @@@ void exit_signals(struct task_struct *t
  out:
        spin_unlock_irq(&tsk->sighand->siglock);
  
 -      if (unlikely(group_stop)) {
 +      if (unlikely(group_stop) && tracehook_notify_jctl(1, CLD_STOPPED)) {
                read_lock(&tasklist_lock);
                do_notify_parent_cldstop(tsk, CLD_STOPPED);
                read_unlock(&tasklist_lock);
@@@ -1923,6 -1921,8 +1924,6 @@@ EXPORT_SYMBOL(recalc_sigpending)
  EXPORT_SYMBOL_GPL(dequeue_signal);
  EXPORT_SYMBOL(flush_signals);
  EXPORT_SYMBOL(force_sig);
 -EXPORT_SYMBOL(kill_proc);
 -EXPORT_SYMBOL(ptrace_notify);
  EXPORT_SYMBOL(send_sig);
  EXPORT_SYMBOL(send_sig_info);
  EXPORT_SYMBOL(sigprocmask);
@@@ -2197,7 -2197,7 +2198,7 @@@ sys_rt_sigtimedwait(const sigset_t __us
  }
  
  asmlinkage long
 -sys_kill(int pid, int sig)
 +sys_kill(pid_t pid, int sig)
  {
        struct siginfo info;
  
        return kill_something_info(sig, &info, pid);
  }
  
 -static int do_tkill(int tgid, int pid, int sig)
 +static int do_tkill(pid_t tgid, pid_t pid, int sig)
  {
        int error;
        struct siginfo info;
   *  exists but it's not belonging to the target process anymore. This
   *  method solves the problem of threads exiting and PIDs getting reused.
   */
 -asmlinkage long sys_tgkill(int tgid, int pid, int sig)
 +asmlinkage long sys_tgkill(pid_t tgid, pid_t pid, int sig)
  {
        /* This is only valid for single tasks */
        if (pid <= 0 || tgid <= 0)
   *  Send a signal to only one task, even if it's a CLONE_THREAD task.
   */
  asmlinkage long
 -sys_tkill(int pid, int sig)
 +sys_tkill(pid_t pid, int sig)
  {
        /* This is only valid for single tasks */
        if (pid <= 0)
  }
  
  asmlinkage long
 -sys_rt_sigqueueinfo(int pid, int sig, siginfo_t __user *uinfo)
 +sys_rt_sigqueueinfo(pid_t pid, int sig, siginfo_t __user *uinfo)
  {
        siginfo_t info;
  
@@@ -2326,7 -2326,7 +2327,7 @@@ int do_sigaction(int sig, struct k_siga
                 *   (for example, SIGCHLD), shall cause the pending signal to
                 *   be discarded, whether or not it is blocked"
                 */
 -              if (__sig_ignored(t, sig)) {
 +              if (sig_handler_ignored(sig_handler(t, sig), sig)) {
                        sigemptyset(&mask);
                        sigaddset(&mask, sig);
                        rm_from_queue_full(&mask, &t->signal->shared_pending);