diff options
author | Thomas Gleixner <tglx@linutronix.de> | 2009-09-14 23:37:40 +0200 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2009-09-15 10:17:30 +0200 |
commit | 12e09337fe238981cb0c87543306e23775d1a143 (patch) | |
tree | 4c5052a801e6868ce2c701af10bf55f5ec5a4929 | |
parent | 54a6bc0b071c50150bc6d1da16c2cd9a963e288c (diff) | |
download | lwn-12e09337fe238981cb0c87543306e23775d1a143.tar.gz lwn-12e09337fe238981cb0c87543306e23775d1a143.zip |
time: Prevent 32 bit overflow with set_normalized_timespec()
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>
-rw-r--r-- | include/linux/time.h | 2 | ||||
-rw-r--r-- | kernel/time.c | 9 |
2 files changed, 9 insertions, 2 deletions
diff --git a/include/linux/time.h b/include/linux/time.h index 256232f7e5e6..56787c093345 100644 --- a/include/linux/time.h +++ b/include/linux/time.h @@ -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); diff --git a/kernel/time.c b/kernel/time.c index 29511943871a..2e2e469a7fec 100644 --- a/kernel/time.c +++ b/kernel/time.c @@ -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; } |