Restrict clearing TIF_SIGPENDING
[sfrench/cifs-2.6.git] / kernel / signal.c
index 2ac3a668d9dd6adf8fb943ab8a0894f6b486e3fb..fe590e00db8df24823280b0bbf94a1daead4cfb5 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/syscalls.h>
 #include <linux/ptrace.h>
 #include <linux/signal.h>
+#include <linux/signalfd.h>
 #include <linux/capability.h>
 #include <linux/freezer.h>
 #include <linux/pid_namespace.h>
@@ -95,26 +96,43 @@ static inline int has_pending_signals(sigset_t *signal, sigset_t *blocked)
 
 #define PENDING(p,b) has_pending_signals(&(p)->signal, (b))
 
-fastcall void recalc_sigpending_tsk(struct task_struct *t)
+static int recalc_sigpending_tsk(struct task_struct *t)
 {
        if (t->signal->group_stop_count > 0 ||
            (freezing(t)) ||
            PENDING(&t->pending, &t->blocked) ||
-           PENDING(&t->signal->shared_pending, &t->blocked))
+           PENDING(&t->signal->shared_pending, &t->blocked)) {
                set_tsk_thread_flag(t, TIF_SIGPENDING);
-       else
-               clear_tsk_thread_flag(t, TIF_SIGPENDING);
+               return 1;
+       }
+       /*
+        * We must never clear the flag in another thread, or in current
+        * when it's possible the current syscall is returning -ERESTART*.
+        * So we don't clear it here, and only callers who know they should do.
+        */
+       return 0;
+}
+
+/*
+ * After recalculating TIF_SIGPENDING, we need to make sure the task wakes up.
+ * This is superfluous when called on current, the wakeup is a harmless no-op.
+ */
+void recalc_sigpending_and_wake(struct task_struct *t)
+{
+       if (recalc_sigpending_tsk(t))
+               signal_wake_up(t, 0);
 }
 
 void recalc_sigpending(void)
 {
-       recalc_sigpending_tsk(current);
+       if (!recalc_sigpending_tsk(current))
+               clear_thread_flag(TIF_SIGPENDING);
+
 }
 
 /* Given the mask, find the first available signal that should be serviced. */
 
-static int
-next_signal(struct sigpending *pending, sigset_t *mask)
+int next_signal(struct sigpending *pending, sigset_t *mask)
 {
        unsigned long i, *s, *m, x;
        int sig = 0;
@@ -373,7 +391,8 @@ int dequeue_signal(struct task_struct *tsk, sigset_t *mask, siginfo_t *info)
                        }
                }
        }
-       recalc_sigpending_tsk(tsk);
+       if (likely(tsk == current))
+               recalc_sigpending();
        if (signr && unlikely(sig_kernel_stop(signr))) {
                /*
                 * Set a marker that we have dequeued a stop signal.  Our
@@ -497,6 +516,11 @@ static int check_kill_permission(int sig, struct siginfo *info,
        int error = -EINVAL;
        if (!valid_signal(sig))
                return error;
+
+       error = audit_signal_info(sig, t); /* Let audit system see the signal */
+       if (error)
+               return error;
+
        error = -EPERM;
        if ((info == SEND_SIG_NOINFO || (!is_si_special(info) && SI_FROMUSER(info)))
            && ((sig != SIGCONT) ||
@@ -506,10 +530,7 @@ static int check_kill_permission(int sig, struct siginfo *info,
            && !capable(CAP_KILL))
                return error;
 
-       error = security_task_kill(t, info, sig, 0);
-       if (!error)
-               audit_signal_info(sig, t); /* Let audit system see the signal */
-       return error;
+       return security_task_kill(t, info, sig, 0);
 }
 
 /* forward decl */
@@ -629,6 +650,12 @@ static int send_signal(int sig, struct siginfo *info, struct task_struct *t,
        struct sigqueue * q = NULL;
        int ret = 0;
 
+       /*
+        * Deliver the signal to listening signalfds. This must be called
+        * with the sighand lock held.
+        */
+       signalfd_notify(t, sig);
+
        /*
         * fast-pathed signals for kernel-internal things like SIGSTOP
         * or SIGKILL.
@@ -736,7 +763,7 @@ force_sig_info(int sig, struct siginfo *info, struct task_struct *t)
                action->sa.sa_handler = SIG_DFL;
                if (blocked) {
                        sigdelset(&t->blocked, sig);
-                       recalc_sigpending_tsk(t);
+                       recalc_sigpending_and_wake(t);
                }
        }
        ret = specific_send_sig_info(sig, info, t);
@@ -1280,6 +1307,11 @@ int send_sigqueue(int sig, struct sigqueue *q, struct task_struct *p)
                ret = 1;
                goto out;
        }
+       /*
+        * Deliver the signal to listening signalfds. This must be called
+        * with the sighand lock held.
+        */
+       signalfd_notify(p, sig);
 
        list_add_tail(&q->list, &p->pending.list);
        sigaddset(&p->pending.signal, sig);
@@ -1323,6 +1355,11 @@ send_group_sigqueue(int sig, struct sigqueue *q, struct task_struct *p)
                q->info.si_overrun++;
                goto out;
        } 
+       /*
+        * Deliver the signal to listening signalfds. This must be called
+        * with the sighand lock held.
+        */
+       signalfd_notify(p, sig);
 
        /*
         * Put this signal on the shared-pending queue.
@@ -1550,8 +1587,9 @@ static void ptrace_stop(int exit_code, int nostop_code, siginfo_t *info)
        /*
         * Queued signals ignored us while we were stopped for tracing.
         * So check for any that we should take before resuming user mode.
+        * This sets TIF_SIGPENDING, but never clears it.
         */
-       recalc_sigpending();
+       recalc_sigpending_tsk(current);
 }
 
 void ptrace_notify(int exit_code)
@@ -1983,6 +2021,8 @@ int copy_siginfo_to_user(siginfo_t __user *to, siginfo_t *from)
        /*
         * If you change siginfo_t structure, please be sure
         * this code is fixed accordingly.
+        * Please remember to update the signalfd_copyinfo() function
+        * inside fs/signalfd.c too, in case siginfo_t changes.
         * It should never copy any pad contained in the structure
         * to avoid security leaks, but must copy the generic
         * 3 ints plus the relevant union member.
@@ -2253,7 +2293,7 @@ int do_sigaction(int sig, struct k_sigaction *act, struct k_sigaction *oact)
                        rm_from_queue_full(&mask, &t->signal->shared_pending);
                        do {
                                rm_from_queue_full(&mask, &t->pending);
-                               recalc_sigpending_tsk(t);
+                               recalc_sigpending_and_wake(t);
                                t = next_thread(t);
                        } while (t != current);
                }