Commit 0097c18e authored by Linus Torvalds's avatar Linus Torvalds
Browse files

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
parents a33d946c d125d134
Loading
Loading
Loading
Loading
+29 −4
Original line number Diff line number Diff line
@@ -470,11 +470,35 @@ u64 alarm_forward(struct alarm *alarm, ktime_t now, ktime_t interval)
}
EXPORT_SYMBOL_GPL(alarm_forward);

u64 alarm_forward_now(struct alarm *alarm, ktime_t interval)
static u64 __alarm_forward_now(struct alarm *alarm, ktime_t interval, bool throttle)
{
	struct alarm_base *base = &alarm_bases[alarm->type];
	ktime_t now = base->get_ktime();

	if (IS_ENABLED(CONFIG_HIGH_RES_TIMERS) && throttle) {
		/*
		 * Same issue as with posix_timer_fn(). Timers which are
		 * periodic but the signal is ignored can starve the system
		 * with a very small interval. The real fix which was
		 * promised in the context of posix_timer_fn() never
		 * materialized, but someone should really work on it.
		 *
		 * To prevent DOS fake @now to be 1 jiffie out which keeps
		 * the overrun accounting correct but creates an
		 * inconsistency vs. timer_gettime(2).
		 */
		ktime_t kj = NSEC_PER_SEC / HZ;

	return alarm_forward(alarm, base->get_ktime(), interval);
		if (interval < kj)
			now = ktime_add(now, kj);
	}

	return alarm_forward(alarm, now, interval);
}

u64 alarm_forward_now(struct alarm *alarm, ktime_t interval)
{
	return __alarm_forward_now(alarm, interval, false);
}
EXPORT_SYMBOL_GPL(alarm_forward_now);

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