Commit 8bfd9a7a authored by Thomas Gleixner's avatar Thomas Gleixner Committed by Linus Torvalds

[PATCH] hrtimers: prevent possible itimer DoS

Fix potential setitimer DoS with high-res timers by pushing itimer rearm
processing to process context.

[Fixes from: Ingo Molnar <mingo@elte.hu>]
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
Cc: john stultz <johnstul@us.ibm.com>
Cc: Roman Zippel <zippel@linux-m68k.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 54cdfdb4
...@@ -135,11 +135,6 @@ enum hrtimer_restart it_real_fn(struct hrtimer *timer) ...@@ -135,11 +135,6 @@ enum hrtimer_restart it_real_fn(struct hrtimer *timer)
send_group_sig_info(SIGALRM, SEND_SIG_PRIV, sig->tsk); send_group_sig_info(SIGALRM, SEND_SIG_PRIV, sig->tsk);
if (sig->it_real_incr.tv64 != 0) {
hrtimer_forward(timer, hrtimer_cb_get_time(timer),
sig->it_real_incr);
return HRTIMER_RESTART;
}
return HRTIMER_NORESTART; return HRTIMER_NORESTART;
} }
...@@ -231,11 +226,14 @@ int do_setitimer(int which, struct itimerval *value, struct itimerval *ovalue) ...@@ -231,11 +226,14 @@ int do_setitimer(int which, struct itimerval *value, struct itimerval *ovalue)
spin_unlock_irq(&tsk->sighand->siglock); spin_unlock_irq(&tsk->sighand->siglock);
goto again; goto again;
} }
tsk->signal->it_real_incr =
timeval_to_ktime(value->it_interval);
expires = timeval_to_ktime(value->it_value); expires = timeval_to_ktime(value->it_value);
if (expires.tv64 != 0) if (expires.tv64 != 0) {
tsk->signal->it_real_incr =
timeval_to_ktime(value->it_interval);
hrtimer_start(timer, expires, HRTIMER_MODE_REL); hrtimer_start(timer, expires, HRTIMER_MODE_REL);
} else
tsk->signal->it_real_incr.tv64 = 0;
spin_unlock_irq(&tsk->sighand->siglock); spin_unlock_irq(&tsk->sighand->siglock);
break; break;
case ITIMER_VIRTUAL: case ITIMER_VIRTUAL:
......
...@@ -456,26 +456,50 @@ static int __dequeue_signal(struct sigpending *pending, sigset_t *mask, ...@@ -456,26 +456,50 @@ static int __dequeue_signal(struct sigpending *pending, sigset_t *mask,
int dequeue_signal(struct task_struct *tsk, sigset_t *mask, siginfo_t *info) int dequeue_signal(struct task_struct *tsk, sigset_t *mask, siginfo_t *info)
{ {
int signr = __dequeue_signal(&tsk->pending, mask, info); int signr = __dequeue_signal(&tsk->pending, mask, info);
if (!signr) if (!signr) {
signr = __dequeue_signal(&tsk->signal->shared_pending, signr = __dequeue_signal(&tsk->signal->shared_pending,
mask, info); mask, info);
/*
* 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.tv64 != 0) {
hrtimer_forward(tmr, tmr->base->get_time(),
tsk->signal->it_real_incr);
hrtimer_restart(tmr);
}
}
}
recalc_sigpending_tsk(tsk); recalc_sigpending_tsk(tsk);
if (signr && unlikely(sig_kernel_stop(signr))) { if (signr && unlikely(sig_kernel_stop(signr))) {
/* /*
* Set a marker that we have dequeued a stop signal. Our * Set a marker that we have dequeued a stop signal. Our
* caller might release the siglock and then the pending * caller might release the siglock and then the pending
* stop signal it is about to process is no longer in the * stop signal it is about to process is no longer in the
* pending bitmasks, but must still be cleared by a SIGCONT * pending bitmasks, but must still be cleared by a SIGCONT
* (and overruled by a SIGKILL). So those cases clear this * (and overruled by a SIGKILL). So those cases clear this
* shared flag after we've set it. Note that this flag may * shared flag after we've set it. Note that this flag may
* remain set after the signal we return is ignored or * remain set after the signal we return is ignored or
* handled. That doesn't matter because its only purpose * handled. That doesn't matter because its only purpose
* is to alert stop-signal processing code when another * is to alert stop-signal processing code when another
* processor has come along and cleared the flag. * processor has come along and cleared the flag.
*/ */
if (!(tsk->signal->flags & SIGNAL_GROUP_EXIT)) if (!(tsk->signal->flags & SIGNAL_GROUP_EXIT))
tsk->signal->flags |= SIGNAL_STOP_DEQUEUED; tsk->signal->flags |= SIGNAL_STOP_DEQUEUED;
} }
if ( signr && if ( signr &&
((info->si_code & __SI_MASK) == __SI_TIMER) && ((info->si_code & __SI_MASK) == __SI_TIMER) &&
info->si_sys_private){ info->si_sys_private){
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment