time: Prevent 32 bit overflow with set_normalized_timespec()
authorThomas Gleixner <tglx@linutronix.de>
Mon, 14 Sep 2009 21:37:40 +0000 (23:37 +0200)
committerThomas Gleixner <tglx@linutronix.de>
Tue, 15 Sep 2009 08:17:30 +0000 (10:17 +0200)
set_normalized_timespec() nsec argument is of type long. The recent
timekeeping changes of ktime_get_ts() feed

ts->tv_nsec + tomono.tv_nsec + nsecs

to set_normalized_timespec(). On 32 bit machines that sum can be
larger than (1 << 31) and therefor result in a negative value which
screws up the result completely.

Make the nsec argument of set_normalized_timespec() s64 to fix the
problem at hand. This also prevents similar problems for future users
of set_normalized_timespec().

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Tested-by: Carsten Emde <carsten.emde@osadl.org>
LKML-Reference: <new-submission>
Cc: Martin Schwidefsky <schwidefsky@de.ibm.com>
Cc: John Stultz <johnstul@us.ibm.com>
include/linux/time.h
kernel/time.c

index 256232f7e5e6e3022f9d66b3e228b6aa6d9e8910..56787c0933456c560bc98e860da4a989e8437f18 100644 (file)
@@ -75,7 +75,7 @@ extern unsigned long mktime(const unsigned int year, const unsigned int mon,
                            const unsigned int day, const unsigned int hour,
                            const unsigned int min, const unsigned int sec);
 
-extern void set_normalized_timespec(struct timespec *ts, time_t sec, long nsec);
+extern void set_normalized_timespec(struct timespec *ts, time_t sec, s64 nsec);
 extern struct timespec timespec_add_safe(const struct timespec lhs,
                                         const struct timespec rhs);
 
index 29511943871a15b2acf87cccf528513b07e57880..2e2e469a7fecea49ea9124eaedf5b025075d789e 100644 (file)
@@ -370,13 +370,20 @@ EXPORT_SYMBOL(mktime);
  *     0 <= tv_nsec < NSEC_PER_SEC
  * For negative values only the tv_sec field is negative !
  */
-void set_normalized_timespec(struct timespec *ts, time_t sec, long nsec)
+void set_normalized_timespec(struct timespec *ts, time_t sec, s64 nsec)
 {
        while (nsec >= NSEC_PER_SEC) {
+               /*
+                * The following asm() prevents the compiler from
+                * optimising this loop into a modulo operation. See
+                * also __iter_div_u64_rem() in include/linux/time.h
+                */
+               asm("" : "+rm"(nsec));
                nsec -= NSEC_PER_SEC;
                ++sec;
        }
        while (nsec < 0) {
+               asm("" : "+rm"(nsec));
                nsec += NSEC_PER_SEC;
                --sec;
        }