CVE-2011-3209 (C, published 2012-10-03T11:02Z)
Description: value: The div_long_long_rem implementation in include/asm-x86/div64.h in the Linux kernel before 2.6.26 on the x86 platform allows local users to cause a denial of service (Divide Error Fault and panic) via a clock_gettime system call.
Severity: MEDIUM (CVSS v3 base score: )
Weakness: CWE-189 \u2014 Numeric Errors
Weaknesses in this category are related to improper calculation or conversion of numbers.
Repository: https://github.com/torvalds/linux
Files changed:
arch/mips/kernel/binfmt_elfn32.c
arch/mips/kernel/binfmt_elfo32.c
drivers/char/mmtimer.c
include/asm-x86/div64.h
include/linux/jiffies.h
kernel/posix-cpu-timers.c
kernel/time.c
kernel/time/ntp.c
mm/slub.c
Commit message: remove div_long_long_rem

x86 is the only arch right now, which provides an optimized for
div_long_long_rem and it has the downside that one has to be very careful that
the divide doesn't overflow.

The API is a little akward, as the arguments for the unsigned divide are
signed.  The signed version also doesn't handle a negative divisor and
produces worse code on 64bit archs.

There is little incentive to keep this API alive, so this converts the few
users to the new API.

Signed-off-by: Roman Zippel <zippel@linux-m68k.org>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: john stultz <johnstul@us.ibm.com>
Cc: Christoph Lameter <clameter@sgi.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>


Vulnerable code:
	long rem;
	value->tv_sec = div_long_long_rem(nsec, NSEC_PER_SEC, &rem);
	long rem;
	value->tv_sec = div_long_long_rem(nsec, NSEC_PER_SEC, &rem);
	tp->tv_sec = div_long_long_rem(nsec, NSEC_PER_SEC, &tp->tv_nsec)
			+ sgi_clock_offset.tv_sec;
	u64 rem;
	sgi_clock_offset.tv_sec = tp->tv_sec - div_long_long_rem(nsec, NSEC_PER_SEC, &rem);
#define timespec_to_ns(x) ((x).tv_nsec + (x).tv_sec * NSEC_PER_SEC)
#define ns_to_timespec(ts, nsec) (ts).tv_sec = div_long_long_rem(nsec, NSEC_PER_SEC, &(ts).tv_nsec)

	ns_to_timespec(cur_setting->it_interval, timr->it.mmtimer.incr * sgi_clock_period);
	ns_to_timespec(cur_setting->it_value, (timr->it.mmtimer.expires - rtc_time())* sgi_clock_period);
	return;
	when = timespec_to_ns(new_setting->it_value);
	period = timespec_to_ns(new_setting->it_interval);
		now = timespec_to_ns(n);
/*
 * (long)X = ((long long)divs) / (long)div
 * (long)rem = ((long long)divs) % (long)div
 *
 * Warning, this will do an exception if X overflows.
 */
#define div_long_long_rem(a, b, c) div_ll_X_l_rem(a, b, c)

static inline long div_ll_X_l_rem(long long divs, long div, long *rem)
{
	long dum2;
	asm("divl %2":"=a"(dum2), "=d"(*rem)
	    : "rm"(div), "A"(divs));

	return dum2;

}

#ifndef _LINUX_CALC64_H
#define _LINUX_CALC64_H

#include <linux/types.h>
#include <asm/div64.h>

/*
 * This is a generic macro which is used when the architecture
 * specific div64.h does not provide a optimized one.
 *
 * The 64bit dividend is divided by the divisor (data type long), the
 * result is returned and the remainder stored in the variable
 * referenced by remainder (data type long *). In contrast to the
 * do_div macro the dividend is kept intact.
 */
#ifndef div_long_long_rem
#define div_long_long_rem(dividend, divisor, remainder)	\
	do_div_llr((dividend), divisor, remainder)

static inline unsigned long do_div_llr(const long long dividend,
				       const long divisor, long *remainder)
{
	u64 result = dividend;

	*(remainder) = do_div(result, divisor);
	return (unsigned long) result;
}
#endif

/*
 * Sign aware variation of the above. On some architectures a
 * negative dividend leads to an divide overflow exception, which
 * is avoided by the sign check.
 */
static inline long div_long_long_rem_signed(const long long dividend,
					    const long divisor, long *remainder)
{
	long res;

	if (unlikely(dividend < 0)) {
		res = -div_long_long_rem(-dividend, divisor, remainder);
		*remainder = -(*remainder);
	} else
		res = div_long_long_rem(dividend, divisor, remainder);

	return res;
}

#endif
#include <linux/calc64.h>
#include <asm/uaccess.h>
	if (CPUCLOCK_WHICH(which_clock) == CPUCLOCK_SCHED) {
		tp->tv_sec = div_long_long_rem(cpu.sched,
					       NSEC_PER_SEC, &tp->tv_nsec);
	} else {
	}
	ts.tv_sec = div_long_long_rem_signed(nsec, NSEC_PER_SEC, &ts.tv_nsec);
	if (unlikely(nsec < 0))
		set_normalized_timespec(&ts, ts.tv_sec, ts.tv_nsec);
	u64 nsec = (u64)jiffies * TICK_NSEC;
	value->tv_sec = div_long_long_rem(nsec, NSEC_PER_SEC, &value->tv_nsec);
	u64 nsec = (u64)jiffies * TICK_NSEC;
	long tv_usec;
	value->tv_sec = div_long_long_rem(nsec, NSEC_PER_SEC, &tv_usec);
	tv_usec /= NSEC_PER_USEC;
	value->tv_usec = tv_usec;
	long mtemp, save_adjust, rem;
		    time_offset = div_long_long_rem_signed(time_offset,
							   NTP_INTERVAL_FREQ,
							   &rem);
			unsigned long remainder;

			l->min_time,
			div_long_long_rem(l->sum_time, l->count, &remainder),
			l->max_time);

Fixed code:
#include <linux/math64.h>
	u32 rem;
	value->tv_sec = div_u64_rem(nsec, NSEC_PER_SEC, &rem);
#include <linux/math64.h>
	u32 rem;
	value->tv_sec = div_u64_rem(nsec, NSEC_PER_SEC, &rem);
#include <linux/time.h>
#include <linux/math64.h>
	*tp = ns_to_timespec(nsec);
	tp->tv_sec += sgi_clock_offset.tv_sec;
	u32 rem;
	sgi_clock_offset.tv_sec = tp->tv_sec - div_u64_rem(nsec, NSEC_PER_SEC, &rem);
	cur_setting->it_interval = ns_to_timespec(timr->it.mmtimer.incr * sgi_clock_period);
	cur_setting->it_value = ns_to_timespec((timr->it.mmtimer.expires - rtc_time()) * sgi_clock_period);
	when = timespec_to_ns(&new_setting->it_value);
	period = timespec_to_ns(&new_setting->it_interval);
		now = timespec_to_ns(&n);
#include <linux/math64.h>
#include <linux/math64.h>
#include <asm/uaccess.h>
	if (CPUCLOCK_WHICH(which_clock) == CPUCLOCK_SCHED)
		*tp = ns_to_timespec(cpu.sched);
	else
	s32 rem;
	ts.tv_sec = div_s64_rem(nsec, NSEC_PER_SEC, &rem);
	if (unlikely(rem < 0)) {
		ts.tv_sec--;
		rem += NSEC_PER_SEC;
	}
	ts.tv_nsec = rem;
	u32 rem;
	value->tv_sec = div_u64_rem((u64)jiffies * TICK_NSEC,
				    NSEC_PER_SEC, &rem);
	value->tv_nsec = rem;
	u32 rem;
	value->tv_sec = div_u64_rem((u64)jiffies * TICK_NSEC,
				    NSEC_PER_SEC, &rem);
	value->tv_usec = rem / NSEC_PER_USEC;
	long mtemp, save_adjust;
		    time_offset = div_s64(time_offset, NTP_INTERVAL_FREQ);
#include <linux/math64.h>
				l->min_time,
				(long)div_u64(l->sum_time, l->count),
				l->max_time);