Skip to content

Commit 5a7780e

Browse files
committed
hrtimer: check relative timeouts for overflow
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 <[email protected]> Acked-by: Ingo Molnar <[email protected]> Tested-by: Frans Pop <[email protected]>
1 parent e760e71 commit 5a7780e

File tree

5 files changed

+29
-22
lines changed

5 files changed

+29
-22
lines changed

include/linux/ktime.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,8 @@ static inline ktime_t ktime_sub_us(const ktime_t kt, const u64 usec)
310310
return ktime_sub_ns(kt, usec * 1000);
311311
}
312312

313+
extern ktime_t ktime_add_safe(const ktime_t lhs, const ktime_t rhs);
314+
313315
/*
314316
* The resolution of the clocks. The resolution value is returned in
315317
* the clock_getres() system call to give application programmers an

kernel/futex.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2116,7 +2116,7 @@ asmlinkage long sys_futex(u32 __user *uaddr, int op, u32 val,
21162116

21172117
t = timespec_to_ktime(ts);
21182118
if (cmd == FUTEX_WAIT)
2119-
t = ktime_add(ktime_get(), t);
2119+
t = ktime_add_safe(ktime_get(), t);
21202120
tp = &t;
21212121
}
21222122
/*

kernel/futex_compat.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ asmlinkage long compat_sys_futex(u32 __user *uaddr, int op, u32 val,
176176

177177
t = timespec_to_ktime(ts);
178178
if (cmd == FUTEX_WAIT)
179-
t = ktime_add(ktime_get(), t);
179+
t = ktime_add_safe(ktime_get(), t);
180180
tp = &t;
181181
}
182182
if (cmd == FUTEX_REQUEUE || cmd == FUTEX_CMP_REQUEUE)

kernel/hrtimer.c

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,23 @@ u64 ktime_divns(const ktime_t kt, s64 div)
325325
}
326326
#endif /* BITS_PER_LONG >= 64 */
327327

328+
/*
329+
* Add two ktime values and do a safety check for overflow:
330+
*/
331+
ktime_t ktime_add_safe(const ktime_t lhs, const ktime_t rhs)
332+
{
333+
ktime_t res = ktime_add(lhs, rhs);
334+
335+
/*
336+
* We use KTIME_SEC_MAX here, the maximum timeout which we can
337+
* return to user space in a timespec:
338+
*/
339+
if (res.tv64 < 0 || res.tv64 < lhs.tv64 || res.tv64 < rhs.tv64)
340+
res = ktime_set(KTIME_SEC_MAX, 0);
341+
342+
return res;
343+
}
344+
328345
/*
329346
* Check, whether the timer is on the callback pending list
330347
*/
@@ -682,13 +699,7 @@ u64 hrtimer_forward(struct hrtimer *timer, ktime_t now, ktime_t interval)
682699
*/
683700
orun++;
684701
}
685-
timer->expires = ktime_add(timer->expires, interval);
686-
/*
687-
* Make sure, that the result did not wrap with a very large
688-
* interval.
689-
*/
690-
if (timer->expires.tv64 < 0)
691-
timer->expires = ktime_set(KTIME_SEC_MAX, 0);
702+
timer->expires = ktime_add_safe(timer->expires, interval);
692703

693704
return orun;
694705
}
@@ -839,7 +850,7 @@ hrtimer_start(struct hrtimer *timer, ktime_t tim, const enum hrtimer_mode mode)
839850
new_base = switch_hrtimer_base(timer, base);
840851

841852
if (mode == HRTIMER_MODE_REL) {
842-
tim = ktime_add(tim, new_base->get_time());
853+
tim = ktime_add_safe(tim, new_base->get_time());
843854
/*
844855
* CONFIG_TIME_LOW_RES is a temporary way for architectures
845856
* to signal that they simply return xtime in
@@ -848,16 +859,8 @@ hrtimer_start(struct hrtimer *timer, ktime_t tim, const enum hrtimer_mode mode)
848859
* timeouts. This will go away with the GTOD framework.
849860
*/
850861
#ifdef CONFIG_TIME_LOW_RES
851-
tim = ktime_add(tim, base->resolution);
862+
tim = ktime_add_safe(tim, base->resolution);
852863
#endif
853-
/*
854-
* Careful here: User space might have asked for a
855-
* very long sleep, so the add above might result in a
856-
* negative number, which enqueues the timer in front
857-
* of the queue.
858-
*/
859-
if (tim.tv64 < 0)
860-
tim.tv64 = KTIME_MAX;
861864
}
862865
timer->expires = tim;
863866

kernel/posix-timers.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -767,9 +767,11 @@ common_timer_set(struct k_itimer *timr, int flags,
767767
/* SIGEV_NONE timers are not queued ! See common_timer_get */
768768
if (((timr->it_sigev_notify & ~SIGEV_THREAD_ID) == SIGEV_NONE)) {
769769
/* Setup correct expiry time for relative timers */
770-
if (mode == HRTIMER_MODE_REL)
771-
timer->expires = ktime_add(timer->expires,
772-
timer->base->get_time());
770+
if (mode == HRTIMER_MODE_REL) {
771+
timer->expires =
772+
ktime_add_safe(timer->expires,
773+
timer->base->get_time());
774+
}
773775
return 0;
774776
}
775777

0 commit comments

Comments
 (0)