mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-06 05:13:18 +00:00
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:
parent
92b043fd99
commit
68f99be287
@ -100,6 +100,8 @@ static inline void posix_cputimers_rt_watchdog(struct posix_cputimers *pct,
|
|||||||
{
|
{
|
||||||
pct->bases[CPUCLOCK_SCHED].nextevt = runtime;
|
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 */
|
/* Init task static initializer */
|
||||||
#define INIT_CPU_TIMERBASE(b) { \
|
#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_init(struct posix_cputimers *pct) { }
|
||||||
static inline void posix_cputimers_group_init(struct posix_cputimers *pct,
|
static inline void posix_cputimers_group_init(struct posix_cputimers *pct,
|
||||||
u64 cpu_limit) { }
|
u64 cpu_limit) { }
|
||||||
|
static inline void posixtimer_rearm_itimer(struct task_struct *p) { }
|
||||||
|
static inline void posixtimer_rearm(struct kernel_siginfo *info) { }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_POSIX_CPU_TIMERS_TASK_WORK
|
#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);
|
int update_rlimit_cpu(struct task_struct *task, unsigned long rlim_new);
|
||||||
|
|
||||||
void posixtimer_rearm(struct kernel_siginfo *info);
|
|
||||||
#endif
|
#endif
|
||||||
|
123
kernel/signal.c
123
kernel/signal.c
@ -478,42 +478,6 @@ void flush_signals(struct task_struct *t)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(flush_signals);
|
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)
|
void ignore_signals(struct task_struct *t)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
@ -636,31 +600,9 @@ int dequeue_signal(sigset_t *mask, kernel_siginfo_t *info, enum pid_type *type)
|
|||||||
*type = PIDTYPE_TGID;
|
*type = PIDTYPE_TGID;
|
||||||
signr = __dequeue_signal(&tsk->signal->shared_pending,
|
signr = __dequeue_signal(&tsk->signal->shared_pending,
|
||||||
mask, info, &resched_timer);
|
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) &&
|
if (unlikely(signr == SIGALRM))
|
||||||
tsk->signal->it_real_incr != 0) {
|
posixtimer_rearm_itimer(tsk);
|
||||||
hrtimer_forward(tmr, tmr->base->get_time(),
|
|
||||||
tsk->signal->it_real_incr);
|
|
||||||
hrtimer_restart(tmr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
recalc_sigpending();
|
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;
|
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 */
|
if (IS_ENABLED(CONFIG_POSIX_TIMERS)) {
|
||||||
info->si_sys_private = 0;
|
if (unlikely(resched_timer))
|
||||||
|
posixtimer_rearm(info);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
return signr;
|
return signr;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(dequeue_signal);
|
EXPORT_SYMBOL_GPL(dequeue_signal);
|
||||||
@ -1922,15 +1854,43 @@ int kill_pid(struct pid *pid, int sig, int priv)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(kill_pid);
|
EXPORT_SYMBOL(kill_pid);
|
||||||
|
|
||||||
|
#ifdef CONFIG_POSIX_TIMERS
|
||||||
/*
|
/*
|
||||||
* These functions support sending signals using preallocated sigqueue
|
* These functions handle POSIX timer signals. POSIX timers use
|
||||||
* structures. This is needed "because realtime applications cannot
|
* preallocated sigqueue structs for sending signals.
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
|
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)
|
struct sigqueue *sigqueue_alloc(void)
|
||||||
{
|
{
|
||||||
return __sigqueue_alloc(-1, current, GFP_KERNEL, 0, SIGQUEUE_PREALLOC);
|
return __sigqueue_alloc(-1, current, GFP_KERNEL, 0, SIGQUEUE_PREALLOC);
|
||||||
@ -2027,6 +1987,7 @@ int send_sigqueue(struct sigqueue *q, struct pid *pid, enum pid_type type)
|
|||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
#endif /* CONFIG_POSIX_TIMERS */
|
||||||
|
|
||||||
void do_notify_pidfd(struct task_struct *task)
|
void do_notify_pidfd(struct task_struct *task)
|
||||||
{
|
{
|
||||||
|
@ -151,7 +151,27 @@ COMPAT_SYSCALL_DEFINE2(getitimer, int, which,
|
|||||||
#endif
|
#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)
|
enum hrtimer_restart it_real_fn(struct hrtimer *timer)
|
||||||
{
|
{
|
||||||
|
@ -251,7 +251,7 @@ static void common_hrtimer_rearm(struct k_itimer *timr)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* This function is called from the signal delivery code if
|
* 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.
|
* be rearmed. Restart the timer and update info::si_overrun.
|
||||||
*/
|
*/
|
||||||
void posixtimer_rearm(struct kernel_siginfo *info)
|
void posixtimer_rearm(struct kernel_siginfo *info)
|
||||||
@ -259,9 +259,15 @@ void posixtimer_rearm(struct kernel_siginfo *info)
|
|||||||
struct k_itimer *timr;
|
struct k_itimer *timr;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Release siglock to ensure proper locking order versus
|
||||||
|
* timr::it_lock. Keep interrupts disabled.
|
||||||
|
*/
|
||||||
|
spin_unlock(¤t->sighand->siglock);
|
||||||
|
|
||||||
timr = lock_timer(info->si_tid, &flags);
|
timr = lock_timer(info->si_tid, &flags);
|
||||||
if (!timr)
|
if (!timr)
|
||||||
return;
|
goto out;
|
||||||
|
|
||||||
if (timr->it_interval && timr->it_requeue_pending == info->si_sys_private) {
|
if (timr->it_interval && timr->it_requeue_pending == info->si_sys_private) {
|
||||||
timr->kclock->timer_rearm(timr);
|
timr->kclock->timer_rearm(timr);
|
||||||
@ -275,6 +281,11 @@ void posixtimer_rearm(struct kernel_siginfo *info)
|
|||||||
}
|
}
|
||||||
|
|
||||||
unlock_timer(timr, flags);
|
unlock_timer(timr, flags);
|
||||||
|
out:
|
||||||
|
spin_lock(¤t->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)
|
int posix_timer_queue_signal(struct k_itimer *timr)
|
||||||
|
Loading…
Reference in New Issue
Block a user