signal: Confine POSIX_TIMERS properly

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
This commit is contained in:
Thomas Gleixner 2024-10-01 10:42:00 +02:00
parent 92b043fd99
commit 68f99be287
4 changed files with 80 additions and 85 deletions

View 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

View 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)
{

View 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)
{

View 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)