diff options
author | Thomas Gleixner <tglx@linutronix.de> | 2008-02-20 01:03:00 +0100 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2008-02-25 16:14:07 -0800 |
commit | ffff62030c0b7de563322eb18fc3fe3fdc01e8de (patch) | |
tree | 923cff1847fd7341506ce1f9a0b8a95a19737dce | |
parent | 2bacfdb30305ffdea047ce860184ad0e570744b5 (diff) | |
download | linux-stable-ffff62030c0b7de563322eb18fc3fe3fdc01e8de.tar.gz linux-stable-ffff62030c0b7de563322eb18fc3fe3fdc01e8de.tar.bz2 linux-stable-ffff62030c0b7de563322eb18fc3fe3fdc01e8de.zip |
hrtimer: check relative timeouts for overflow
commit: 5a7780e725d1bb4c3094fcc12f1c5c5faea1e988
Various user space callers ask for relative timeouts. While we fixed
that overflow issue in hrtimer_start(), the sites which convert
relative user space values to absolute timeouts themself were uncovered.
Instead of putting overflow checks into each place add a function
which does the sanity checking and convert all affected callers to use
it.
Thanks to Frans Pop, who reported the problem and tested the fixes.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Acked-by: Ingo Molnar <mingo@elte.hu>
Tested-by: Frans Pop <elendil@planet.nl>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r-- | include/linux/ktime.h | 2 | ||||
-rw-r--r-- | kernel/futex.c | 2 | ||||
-rw-r--r-- | kernel/futex_compat.c | 2 | ||||
-rw-r--r-- | kernel/hrtimer.c | 38 | ||||
-rw-r--r-- | kernel/posix-timers.c | 8 |
5 files changed, 30 insertions, 22 deletions
diff --git a/include/linux/ktime.h b/include/linux/ktime.h index dae7143644fe..15a0229120c5 100644 --- a/include/linux/ktime.h +++ b/include/linux/ktime.h @@ -289,6 +289,8 @@ static inline ktime_t ktime_add_us(const ktime_t kt, const u64 usec) return ktime_add_ns(kt, usec * 1000); } +extern ktime_t ktime_add_safe(const ktime_t lhs, const ktime_t rhs); + /* * The resolution of the clocks. The resolution value is returned in * the clock_getres() system call to give application programmers an diff --git a/kernel/futex.c b/kernel/futex.c index b658a9a4a22c..0c55a5860ada 100644 --- a/kernel/futex.c +++ b/kernel/futex.c @@ -2063,7 +2063,7 @@ asmlinkage long sys_futex(u32 __user *uaddr, int op, u32 val, t = timespec_to_ktime(ts); if (cmd == FUTEX_WAIT) - t = ktime_add(ktime_get(), t); + t = ktime_add_safe(ktime_get(), t); tp = &t; } /* diff --git a/kernel/futex_compat.c b/kernel/futex_compat.c index f938c233d5dc..bba74b6a970a 100644 --- a/kernel/futex_compat.c +++ b/kernel/futex_compat.c @@ -175,7 +175,7 @@ asmlinkage long compat_sys_futex(u32 __user *uaddr, int op, u32 val, t = timespec_to_ktime(ts); if (cmd == FUTEX_WAIT) - t = ktime_add(ktime_get(), t); + t = ktime_add_safe(ktime_get(), t); tp = &t; } if (cmd == FUTEX_REQUEUE || cmd == FUTEX_CMP_REQUEUE) diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c index ee8d0ac1ebd2..2ee0497b42e2 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c @@ -301,6 +301,24 @@ unsigned long ktime_divns(const ktime_t kt, s64 div) } #endif /* BITS_PER_LONG >= 64 */ +/* + * Add two ktime values and do a safety check for overflow: + */ + +ktime_t ktime_add_safe(const ktime_t lhs, const ktime_t rhs) +{ + ktime_t res = ktime_add(lhs, rhs); + + /* + * We use KTIME_SEC_MAX here, the maximum timeout which we can + * return to user space in a timespec: + */ + if (res.tv64 < 0 || res.tv64 < lhs.tv64 || res.tv64 < rhs.tv64) + res = ktime_set(KTIME_SEC_MAX, 0); + + return res; +} + /* High resolution timer related functions */ #ifdef CONFIG_HIGH_RES_TIMERS @@ -658,13 +676,7 @@ hrtimer_forward(struct hrtimer *timer, ktime_t now, ktime_t interval) */ orun++; } - timer->expires = ktime_add(timer->expires, interval); - /* - * Make sure, that the result did not wrap with a very large - * interval. - */ - if (timer->expires.tv64 < 0) - timer->expires = ktime_set(KTIME_SEC_MAX, 0); + timer->expires = ktime_add_safe(timer->expires, interval); return orun; } @@ -815,7 +827,7 @@ hrtimer_start(struct hrtimer *timer, ktime_t tim, const enum hrtimer_mode mode) new_base = switch_hrtimer_base(timer, base); if (mode == HRTIMER_MODE_REL) { - tim = ktime_add(tim, new_base->get_time()); + tim = ktime_add_safe(tim, new_base->get_time()); /* * CONFIG_TIME_LOW_RES is a temporary way for architectures * to signal that they simply return xtime in @@ -824,16 +836,8 @@ hrtimer_start(struct hrtimer *timer, ktime_t tim, const enum hrtimer_mode mode) * timeouts. This will go away with the GTOD framework. */ #ifdef CONFIG_TIME_LOW_RES - tim = ktime_add(tim, base->resolution); + tim = ktime_add_safe(tim, base->resolution); #endif - /* - * Careful here: User space might have asked for a - * very long sleep, so the add above might result in a - * negative number, which enqueues the timer in front - * of the queue. - */ - if (tim.tv64 < 0) - tim.tv64 = KTIME_MAX; } timer->expires = tim; diff --git a/kernel/posix-timers.c b/kernel/posix-timers.c index 7a15afb73ed0..00c9e252d991 100644 --- a/kernel/posix-timers.c +++ b/kernel/posix-timers.c @@ -765,9 +765,11 @@ common_timer_set(struct k_itimer *timr, int flags, /* SIGEV_NONE timers are not queued ! See common_timer_get */ if (((timr->it_sigev_notify & ~SIGEV_THREAD_ID) == SIGEV_NONE)) { /* Setup correct expiry time for relative timers */ - if (mode == HRTIMER_MODE_REL) - timer->expires = ktime_add(timer->expires, - timer->base->get_time()); + if (mode == HRTIMER_MODE_REL) { + timer->expires = + ktime_add_safe(timer->expires, + timer->base->get_time()); + } return 0; } |