diff options
author | Thomas Gleixner <tglx@linutronix.de> | 2009-09-14 17:37:40 -0400 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2009-09-15 04:17:30 -0400 |
commit | 12e09337fe238981cb0c87543306e23775d1a143 (patch) | |
tree | 4c5052a801e6868ce2c701af10bf55f5ec5a4929 | |
parent | 54a6bc0b071c50150bc6d1da16c2cd9a963e288c (diff) |
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, | |||
75 | const unsigned int day, const unsigned int hour, | 75 | const unsigned int day, const unsigned int hour, |
76 | const unsigned int min, const unsigned int sec); | 76 | const unsigned int min, const unsigned int sec); |
77 | 77 | ||
78 | extern void set_normalized_timespec(struct timespec *ts, time_t sec, long nsec); | 78 | extern void set_normalized_timespec(struct timespec *ts, time_t sec, s64 nsec); |
79 | extern struct timespec timespec_add_safe(const struct timespec lhs, | 79 | extern struct timespec timespec_add_safe(const struct timespec lhs, |
80 | const struct timespec rhs); | 80 | const struct timespec rhs); |
81 | 81 | ||
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); | |||
370 | * 0 <= tv_nsec < NSEC_PER_SEC | 370 | * 0 <= tv_nsec < NSEC_PER_SEC |
371 | * For negative values only the tv_sec field is negative ! | 371 | * For negative values only the tv_sec field is negative ! |
372 | */ | 372 | */ |
373 | void set_normalized_timespec(struct timespec *ts, time_t sec, long nsec) | 373 | void set_normalized_timespec(struct timespec *ts, time_t sec, s64 nsec) |
374 | { | 374 | { |
375 | while (nsec >= NSEC_PER_SEC) { | 375 | while (nsec >= NSEC_PER_SEC) { |
376 | /* | ||
377 | * The following asm() prevents the compiler from | ||
378 | * optimising this loop into a modulo operation. See | ||
379 | * also __iter_div_u64_rem() in include/linux/time.h | ||
380 | */ | ||
381 | asm("" : "+rm"(nsec)); | ||
376 | nsec -= NSEC_PER_SEC; | 382 | nsec -= NSEC_PER_SEC; |
377 | ++sec; | 383 | ++sec; |
378 | } | 384 | } |
379 | while (nsec < 0) { | 385 | while (nsec < 0) { |
386 | asm("" : "+rm"(nsec)); | ||
380 | nsec += NSEC_PER_SEC; | 387 | nsec += NSEC_PER_SEC; |
381 | --sec; | 388 | --sec; |
382 | } | 389 | } |