]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
signal: Confine POSIX_TIMERS properly
authorThomas Gleixner <tglx@linutronix.de>
Tue, 1 Oct 2024 08:42:00 +0000 (10:42 +0200)
committerThomas Gleixner <tglx@linutronix.de>
Tue, 29 Oct 2024 10:43:18 +0000 (11:43 +0100)
Move the itimer rearming out of the signal code and consolidate all posix
timer related functions in the signal code under one ifdef.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lore.kernel.org/all/20241001083835.314100569@linutronix.de
include/linux/posix-timers.h
kernel/signal.c
kernel/time/itimer.c
kernel/time/posix-timers.c

index 453691710839645d966af8b852f88af254b0ffc2..670bf03a56ef4324046a1481ffce345e512f85a2 100644 (file)
@@ -100,6 +100,8 @@ static inline void posix_cputimers_rt_watchdog(struct posix_cputimers *pct,
 {
        pct->bases[CPUCLOCK_SCHED].nextevt = runtime;
 }
+void posixtimer_rearm_itimer(struct task_struct *p);
+void posixtimer_rearm(struct kernel_siginfo *info);
 
 /* Init task static initializer */
 #define INIT_CPU_TIMERBASE(b) {                                                \
@@ -122,6 +124,8 @@ struct cpu_timer { };
 static inline void posix_cputimers_init(struct posix_cputimers *pct) { }
 static inline void posix_cputimers_group_init(struct posix_cputimers *pct,
                                              u64 cpu_limit) { }
+static inline void posixtimer_rearm_itimer(struct task_struct *p) { }
+static inline void posixtimer_rearm(struct kernel_siginfo *info) { }
 #endif
 
 #ifdef CONFIG_POSIX_CPU_TIMERS_TASK_WORK
@@ -196,5 +200,4 @@ void set_process_cpu_timer(struct task_struct *task, unsigned int clock_idx,
 
 int update_rlimit_cpu(struct task_struct *task, unsigned long rlim_new);
 
-void posixtimer_rearm(struct kernel_siginfo *info);
 #endif
index 4344860ffcac736c8c2e403a043b487ea55b676d..b65cc1853a093c7c8dcbd2c8c90e75afda0a38d3 100644 (file)
@@ -478,42 +478,6 @@ void flush_signals(struct task_struct *t)
 }
 EXPORT_SYMBOL(flush_signals);
 
-#ifdef CONFIG_POSIX_TIMERS
-static void __flush_itimer_signals(struct sigpending *pending)
-{
-       sigset_t signal, retain;
-       struct sigqueue *q, *n;
-
-       signal = pending->signal;
-       sigemptyset(&retain);
-
-       list_for_each_entry_safe(q, n, &pending->list, list) {
-               int sig = q->info.si_signo;
-
-               if (likely(q->info.si_code != SI_TIMER)) {
-                       sigaddset(&retain, sig);
-               } else {
-                       sigdelset(&signal, sig);
-                       list_del_init(&q->list);
-                       __sigqueue_free(q);
-               }
-       }
-
-       sigorsets(&pending->signal, &signal, &retain);
-}
-
-void flush_itimer_signals(void)
-{
-       struct task_struct *tsk = current;
-       unsigned long flags;
-
-       spin_lock_irqsave(&tsk->sighand->siglock, flags);
-       __flush_itimer_signals(&tsk->pending);
-       __flush_itimer_signals(&tsk->signal->shared_pending);
-       spin_unlock_irqrestore(&tsk->sighand->siglock, flags);
-}
-#endif
-
 void ignore_signals(struct task_struct *t)
 {
        int i;
@@ -636,31 +600,9 @@ int dequeue_signal(sigset_t *mask, kernel_siginfo_t *info, enum pid_type *type)
                *type = PIDTYPE_TGID;
                signr = __dequeue_signal(&tsk->signal->shared_pending,
                                         mask, info, &resched_timer);
-#ifdef CONFIG_POSIX_TIMERS
-               /*
-                * itimer signal ?
-                *
-                * itimers are process shared and we restart periodic
-                * itimers in the signal delivery path to prevent DoS
-                * attacks in the high resolution timer case. This is
-                * compliant with the old way of self-restarting
-                * itimers, as the SIGALRM is a legacy signal and only
-                * queued once. Changing the restart behaviour to
-                * restart the timer in the signal dequeue path is
-                * reducing the timer noise on heavy loaded !highres
-                * systems too.
-                */
-               if (unlikely(signr == SIGALRM)) {
-                       struct hrtimer *tmr = &tsk->signal->real_timer;
-
-                       if (!hrtimer_is_queued(tmr) &&
-                           tsk->signal->it_real_incr != 0) {
-                               hrtimer_forward(tmr, tmr->base->get_time(),
-                                               tsk->signal->it_real_incr);
-                               hrtimer_restart(tmr);
-                       }
-               }
-#endif
+
+               if (unlikely(signr == SIGALRM))
+                       posixtimer_rearm_itimer(tsk);
        }
 
        recalc_sigpending();
@@ -682,22 +624,12 @@ int dequeue_signal(sigset_t *mask, kernel_siginfo_t *info, enum pid_type *type)
                 */
                current->jobctl |= JOBCTL_STOP_DEQUEUED;
        }
-#ifdef CONFIG_POSIX_TIMERS
-       if (resched_timer) {
-               /*
-                * Release the siglock to ensure proper locking order
-                * of timer locks outside of siglocks.  Note, we leave
-                * irqs disabled here, since the posix-timers code is
-                * about to disable them again anyway.
-                */
-               spin_unlock(&tsk->sighand->siglock);
-               posixtimer_rearm(info);
-               spin_lock(&tsk->sighand->siglock);
 
-               /* Don't expose the si_sys_private value to userspace */
-               info->si_sys_private = 0;
+       if (IS_ENABLED(CONFIG_POSIX_TIMERS)) {
+               if (unlikely(resched_timer))
+                       posixtimer_rearm(info);
        }
-#endif
+
        return signr;
 }
 EXPORT_SYMBOL_GPL(dequeue_signal);
@@ -1922,15 +1854,43 @@ int kill_pid(struct pid *pid, int sig, int priv)
 }
 EXPORT_SYMBOL(kill_pid);
 
+#ifdef CONFIG_POSIX_TIMERS
 /*
- * These functions support sending signals using preallocated sigqueue
- * structures.  This is needed "because realtime applications cannot
- * afford to lose notifications of asynchronous events, like timer
- * expirations or I/O completions".  In the case of POSIX Timers
- * we allocate the sigqueue structure from the timer_create.  If this
- * allocation fails we are able to report the failure to the application
- * with an EAGAIN error.
+ * These functions handle POSIX timer signals. POSIX timers use
+ * preallocated sigqueue structs for sending signals.
  */
+static void __flush_itimer_signals(struct sigpending *pending)
+{
+       sigset_t signal, retain;
+       struct sigqueue *q, *n;
+
+       signal = pending->signal;
+       sigemptyset(&retain);
+
+       list_for_each_entry_safe(q, n, &pending->list, list) {
+               int sig = q->info.si_signo;
+
+               if (likely(q->info.si_code != SI_TIMER)) {
+                       sigaddset(&retain, sig);
+               } else {
+                       sigdelset(&signal, sig);
+                       list_del_init(&q->list);
+                       __sigqueue_free(q);
+               }
+       }
+
+       sigorsets(&pending->signal, &signal, &retain);
+}
+
+void flush_itimer_signals(void)
+{
+       struct task_struct *tsk = current;
+
+       guard(spinlock_irqsave)(&tsk->sighand->siglock);
+       __flush_itimer_signals(&tsk->pending);
+       __flush_itimer_signals(&tsk->signal->shared_pending);
+}
+
 struct sigqueue *sigqueue_alloc(void)
 {
        return __sigqueue_alloc(-1, current, GFP_KERNEL, 0, SIGQUEUE_PREALLOC);
@@ -2027,6 +1987,7 @@ ret:
        rcu_read_unlock();
        return ret;
 }
+#endif /* CONFIG_POSIX_TIMERS */
 
 void do_notify_pidfd(struct task_struct *task)
 {
index 00629e658ca196ec1b2c06c4d15fd7e700903ac2..876d389b2e219a10c0dbddb6a06dd3252c502e99 100644 (file)
@@ -151,7 +151,27 @@ COMPAT_SYSCALL_DEFINE2(getitimer, int, which,
 #endif
 
 /*
- * The timer is automagically restarted, when interval != 0
+ * Invoked from dequeue_signal() when SIG_ALRM is delivered.
+ *
+ * Restart the ITIMER_REAL timer if it is armed as periodic timer.  Doing
+ * this in the signal delivery path instead of self rearming prevents a DoS
+ * with small increments in the high reolution timer case and reduces timer
+ * noise in general.
+ */
+void posixtimer_rearm_itimer(struct task_struct *tsk)
+{
+       struct hrtimer *tmr = &tsk->signal->real_timer;
+
+       if (!hrtimer_is_queued(tmr) && tsk->signal->it_real_incr != 0) {
+               hrtimer_forward(tmr, tmr->base->get_time(),
+                               tsk->signal->it_real_incr);
+               hrtimer_restart(tmr);
+       }
+}
+
+/*
+ * Interval timers are restarted in the signal delivery path.  See
+ * posixtimer_rearm_itimer().
  */
 enum hrtimer_restart it_real_fn(struct hrtimer *timer)
 {
index fc40dacabe78220593f45e9d8fd6e8c3a1b7b478..d461a32b7260820c915b468b7b0b53a6679f7523 100644 (file)
@@ -251,7 +251,7 @@ static void common_hrtimer_rearm(struct k_itimer *timr)
 
 /*
  * This function is called from the signal delivery code if
- * info->si_sys_private is not zero, which indicates that the timer has to
+ * info::si_sys_private is not zero, which indicates that the timer has to
  * be rearmed. Restart the timer and update info::si_overrun.
  */
 void posixtimer_rearm(struct kernel_siginfo *info)
@@ -259,9 +259,15 @@ void posixtimer_rearm(struct kernel_siginfo *info)
        struct k_itimer *timr;
        unsigned long flags;
 
+       /*
+        * Release siglock to ensure proper locking order versus
+        * timr::it_lock. Keep interrupts disabled.
+        */
+       spin_unlock(&current->sighand->siglock);
+
        timr = lock_timer(info->si_tid, &flags);
        if (!timr)
-               return;
+               goto out;
 
        if (timr->it_interval && timr->it_requeue_pending == info->si_sys_private) {
                timr->kclock->timer_rearm(timr);
@@ -275,6 +281,11 @@ void posixtimer_rearm(struct kernel_siginfo *info)
        }
 
        unlock_timer(timr, flags);
+out:
+       spin_lock(&current->sighand->siglock);
+
+       /* Don't expose the si_sys_private value to userspace */
+       info->si_sys_private = 0;
 }
 
 int posix_timer_queue_signal(struct k_itimer *timr)