CVE-2018-12896 (C, published 2018-07-02T17:29Z)
Description: value: An issue was discovered in the Linux kernel through 4.17.3. An Integer Overflow in kernel/time/posix-timers.c in the POSIX timer code is caused by the way the overrun accounting works. Depending on interval and expiry time values, the overrun can be larger than INT_MAX, but the accounting is int based. This basically makes the accounting values, which are visible to user space via timer_getoverrun(2) and siginfo::si_overrun, random. For example, a local user can cause a denial of service (signed integer overflow) via crafted mmap, futex, timer_create, and timer_settime system calls.
Severity: LOW (CVSS v3 base score: 5.5)
Weakness: CWE-190 \u2014 Integer Overflow or Wraparound
The product performs a calculation that can
         produce an integer overflow or wraparound when the logic
         assumes that the resulting value will always be larger than
         the original value. This occurs when an integer value is
         incremented to a value that is too large to store in the
         associated representation. When this occurs, the value may
         become a very small or negative number.
Repository: https://github.com/torvalds/linux
Files changed:
include/linux/posix-timers.h
kernel/time/posix-cpu-timers.c
kernel/time/posix-timers.c
Commit message: posix-timers: Sanitize overrun handling

The posix timer overrun handling is broken because the forwarding functions
can return a huge number of overruns which does not fit in an int. As a
consequence timer_getoverrun(2) and siginfo::si_overrun can turn into
random number generators.

The k_clock::timer_forward() callbacks return a 64 bit value now. Make
k_itimer::ti_overrun[_last] 64bit as well, so the kernel internal
accounting is correct. 3Remove the temporary (int) casts.

Add a helper function which clamps the overrun value returned to user space
via timer_getoverrun(2) or siginfo::si_overrun limited to a positive value
between 0 and INT_MAX. INT_MAX is an indicator for user space that the
overrun value has been clamped.

Reported-by: Team OWL337 <icytxw@gmail.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Acked-by: John Stultz <john.stultz@linaro.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Michael Kerrisk <mtk.manpages@gmail.com>
Link: https://lkml.kernel.org/r/20180626132705.018623573@linutronix.de



Vulnerable code:
	int			it_overrun;
	int			it_overrun_last;
		timer->it_overrun += 1 << i;
	timr->it_overrun += (unsigned int) hrtimer_forward(timer,
						timer->base->get_time(),
						timr->it_interval);
		timr->it_overrun = -1;
		info->si_overrun += timr->it_overrun_last;
			timr->it_overrun += (unsigned int)
				hrtimer_forward(timer, now,
						timr->it_interval);
	new_timer->it_overrun = -1;
		timr->it_overrun += (int)kc->timer_forward(timr, now);
	overrun = timr->it_overrun_last;

Fixed code:
	s64			it_overrun;
	s64			it_overrun_last;
		timer->it_overrun += 1LL << i;
/*
 * The siginfo si_overrun field and the return value of timer_getoverrun(2)
 * are of type int. Clamp the overrun value to INT_MAX
 */
static inline int timer_overrun_to_int(struct k_itimer *timr, int baseval)
{
	s64 sum = timr->it_overrun_last + (s64)baseval;

	return sum > (s64)INT_MAX ? INT_MAX : (int)sum;
}

	timr->it_overrun += hrtimer_forward(timer, timer->base->get_time(),
					    timr->it_interval);
		timr->it_overrun = -1LL;
		info->si_overrun = timer_overrun_to_int(timr, info->si_overrun);
			timr->it_overrun += hrtimer_forward(timer, now,
							    timr->it_interval);
	new_timer->it_overrun = -1LL;
		timr->it_overrun += kc->timer_forward(timr, now);
	overrun = timer_overrun_to_int(timr, 0);