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 <[email protected]>
---
kernel/signal.c | 34 ++++++++++++++++++++++++++++------
1 file changed, 28 insertions(+), 6 deletions(-)
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -1965,6 +1965,34 @@ int send_sigqueue(struct sigqueue *q, st
*/
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))
@@ -1972,15 +2000,9 @@ int send_sigqueue(struct sigqueue *q, st
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;