From: Thomas Gleixner <tglx@linutronix.de>
The handling of the timer overrun in the signal code is inconsistent as it
takes previous overruns into account. This is just wrong as after the
reprogramming of a timer the overrun count starts over from a clean state,
i.e. 0.
Make the accounting in send_sigqueue() consistent with that.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
kernel/signal.c | 34 ++++++++++++++++++++++++++++------
1 file changed, 28 insertions(+), 6 deletions(-)
---
diff --git a/kernel/signal.c b/kernel/signal.c
index a407724f1267..a99274287902 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -1961,6 +1961,34 @@ int send_sigqueue(struct sigqueue *q, struct pid *pid, enum pid_type type, int s
*/
q->info.si_sys_private = si_private;
+ /*
+ * Set the overrun count to zero unconditionally. The posix timer
+ * code does not self rearm periodic timers. They are rearmed from
+ * dequeue_signal().
+ *
+ * But there is a situation where @q is already enqueued:
+ *
+ * 1) timer_settime()
+ * arm_timer()
+ * 2) timer_expires()
+ * send_sigqueue(@q)
+ * enqueue(@q)
+ * 3) timer_settime()
+ * arm_timer()
+ * 4) timer_expires()
+ * send_sigqueue(@q) <- Observes @q already queued
+ *
+ * In this case incrementing si_overrun does not make sense because
+ * there is no relationship between timer_settime() #1 and #2.
+ *
+ * The POSIX specification is useful as always: "The effect of
+ * disarming or resetting a timer with pending expiration
+ * notifications is unspecified."
+ *
+ * Just do the sensible thing and reset the overrun.
+ */
+ q->info.si_overrun = 0;
+
ret = 1; /* the signal is ignored */
result = TRACE_SIGNAL_IGNORED;
if (!prepare_signal(sig, t, false))
@@ -1968,15 +1996,9 @@ int send_sigqueue(struct sigqueue *q, struct pid *pid, enum pid_type type, int s
ret = 0;
if (unlikely(!list_empty(&q->list))) {
- /*
- * If an SI_TIMER entry is already queue just increment
- * the overrun count.
- */
- q->info.si_overrun++;
result = TRACE_SIGNAL_ALREADY_PENDING;
goto out;
}
- q->info.si_overrun = 0;
signalfd_notify(t, sig);
pending = (type != PIDTYPE_PID) ? &t->signal->shared_pending : &t->pending;