Skip to content

Commit 0097c18

Browse files
committed
Merge tag 'timers-urgent-2023-02-19' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull timer fix from Thomas Gleixner: "A fix for a long standing issue in the alarmtimer code. Posix-timers armed with a short interval with an ignored signal result in an unpriviledged DoS. Due to the ignored signal the timer switches into self rearm mode. This issue had been "fixed" before but a rework of the alarmtimer code 5 years ago lost that workaround. There is no real good solution for this issue, which is also worked around in the core posix-timer code in the same way, but it certainly moved way up on the ever growing todo list" * tag 'timers-urgent-2023-02-19' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: alarmtimer: Prevent starvation by small intervals and SIG_IGN
2 parents a33d946 + d125d13 commit 0097c18

File tree

1 file changed

+29
-4
lines changed

1 file changed

+29
-4
lines changed

kernel/time/alarmtimer.c

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -470,11 +470,35 @@ u64 alarm_forward(struct alarm *alarm, ktime_t now, ktime_t interval)
470470
}
471471
EXPORT_SYMBOL_GPL(alarm_forward);
472472

473-
u64 alarm_forward_now(struct alarm *alarm, ktime_t interval)
473+
static u64 __alarm_forward_now(struct alarm *alarm, ktime_t interval, bool throttle)
474474
{
475475
struct alarm_base *base = &alarm_bases[alarm->type];
476+
ktime_t now = base->get_ktime();
477+
478+
if (IS_ENABLED(CONFIG_HIGH_RES_TIMERS) && throttle) {
479+
/*
480+
* Same issue as with posix_timer_fn(). Timers which are
481+
* periodic but the signal is ignored can starve the system
482+
* with a very small interval. The real fix which was
483+
* promised in the context of posix_timer_fn() never
484+
* materialized, but someone should really work on it.
485+
*
486+
* To prevent DOS fake @now to be 1 jiffie out which keeps
487+
* the overrun accounting correct but creates an
488+
* inconsistency vs. timer_gettime(2).
489+
*/
490+
ktime_t kj = NSEC_PER_SEC / HZ;
491+
492+
if (interval < kj)
493+
now = ktime_add(now, kj);
494+
}
495+
496+
return alarm_forward(alarm, now, interval);
497+
}
476498

477-
return alarm_forward(alarm, base->get_ktime(), interval);
499+
u64 alarm_forward_now(struct alarm *alarm, ktime_t interval)
500+
{
501+
return __alarm_forward_now(alarm, interval, false);
478502
}
479503
EXPORT_SYMBOL_GPL(alarm_forward_now);
480504

@@ -551,9 +575,10 @@ static enum alarmtimer_restart alarm_handle_timer(struct alarm *alarm,
551575
if (posix_timer_event(ptr, si_private) && ptr->it_interval) {
552576
/*
553577
* Handle ignored signals and rearm the timer. This will go
554-
* away once we handle ignored signals proper.
578+
* away once we handle ignored signals proper. Ensure that
579+
* small intervals cannot starve the system.
555580
*/
556-
ptr->it_overrun += alarm_forward_now(alarm, ptr->it_interval);
581+
ptr->it_overrun += __alarm_forward_now(alarm, ptr->it_interval, true);
557582
++ptr->it_requeue_pending;
558583
ptr->it_active = 1;
559584
result = ALARMTIMER_RESTART;

0 commit comments

Comments
 (0)