[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-ID: <20231219124434.870613-1-simone.weiss@elektrobit.com>
Date: Tue, 19 Dec 2023 13:44:34 +0100
From: Simone Weiss <simone.weiss@...ktrobit.com>
To: <tglx@...utronix.de>
CC: <simone.weiss@...ktrobit.com>, <linux-kernel@...r.kernel.org>
Subject: [PATCH] Fix UBSAN warning for subtracting ktime_t
This issue was found with syzkaller.
UBSAN: Undefined behaviour in kernel/time/hrtimer.c:612:10
signed integer overflow:
9223372036854775807 - -51224496 cannot be represented in type
'long long int'
To fix this issue, add and use a function to check for overflows when
substracting two ktime_t values. This is largly the solution already once
submitted once as RFC. See Link below.
Link: https://lore.kernel.org/lkml/20190306131326.10275-1-yaohongbo@huawei.com/
Signed-off-by: Simone Weiss <simone.weiss@...ktrobit.com>
---
include/linux/ktime.h | 7 +++++++
kernel/time/hrtimer.c | 22 +++++++++++++++++++---
2 files changed, 26 insertions(+), 3 deletions(-)
diff --git a/include/linux/ktime.h b/include/linux/ktime.h
index 73f20deb497d..ed90f53741f4 100644
--- a/include/linux/ktime.h
+++ b/include/linux/ktime.h
@@ -55,6 +55,12 @@ static inline ktime_t ktime_set(const s64 secs, const unsigned long nsecs)
*/
#define ktime_add_unsafe(lhs, rhs) ((u64) (lhs) + (rhs))
+/*
+ * Same as ktime_sub(), but avoids undefined behaviour on overflow; however,
+ * this means that you must check the result for overflow yourself.
+ */
+#define ktime_sub_unsafe(lhs, rhs) ((u64) (lhs) - (rhs))
+
/*
* Add a ktime_t variable and a scalar nanosecond value.
* res = kt + nsval:
@@ -197,6 +203,7 @@ static inline ktime_t ktime_sub_ms(const ktime_t kt, const u64 msec)
}
extern ktime_t ktime_add_safe(const ktime_t lhs, const ktime_t rhs);
+extern ktime_t ktime_sub_safe(const ktime_t lhs, const ktime_t rhs);
/**
* ktime_to_timespec64_cond - convert a ktime_t variable to timespec64
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
index 760793998cdd..cd0534bb60a8 100644
--- a/kernel/time/hrtimer.c
+++ b/kernel/time/hrtimer.c
@@ -195,7 +195,7 @@ hrtimer_check_target(struct hrtimer *timer, struct hrtimer_clock_base *new_base)
{
ktime_t expires;
- expires = ktime_sub(hrtimer_get_expires(timer), new_base->offset);
+ expires = ktime_sub_safe(hrtimer_get_expires(timer), new_base->offset);
return expires < new_base->cpu_base->expires_next;
}
@@ -342,6 +342,22 @@ ktime_t ktime_add_safe(const ktime_t lhs, const ktime_t rhs)
EXPORT_SYMBOL_GPL(ktime_add_safe);
+/*
+ * Sub two ktime values and do a safety check for overflow:
+ */
+ktime_t ktime_sub_safe(const ktime_t lhs, const ktime_t rhs)
+{
+ ktime_t res = ktime_sub_unsafe(lhs, rhs);
+
+ if (lhs > 0 && rhs < 0 && res < 0)
+ res = ktime_set(KTIME_SEC_MAX, 0);
+ else if (lhs < 0 && rhs > 0 && res > 0)
+ res = ktime_set(-KTIME_SEC_MAX, 0);
+
+ return res;
+}
+EXPORT_SYMBOL_GPL(ktime_sub_safe);
+
#ifdef CONFIG_DEBUG_OBJECTS_TIMERS
static const struct debug_obj_descr hrtimer_debug_descr;
@@ -523,7 +539,7 @@ static ktime_t __hrtimer_next_event_base(struct hrtimer_cpu_base *cpu_base,
timer = container_of(next, struct hrtimer, node);
}
- expires = ktime_sub(hrtimer_get_expires(timer), base->offset);
+ expires = ktime_sub_safe(hrtimer_get_expires(timer), base->offset);
if (expires < expires_next) {
expires_next = expires;
@@ -811,7 +827,7 @@ static void hrtimer_reprogram(struct hrtimer *timer, bool reprogram)
{
struct hrtimer_cpu_base *cpu_base = this_cpu_ptr(&hrtimer_bases);
struct hrtimer_clock_base *base = timer->base;
- ktime_t expires = ktime_sub(hrtimer_get_expires(timer), base->offset);
+ ktime_t expires = ktime_sub_safe(hrtimer_get_expires(timer), base->offset);
WARN_ON_ONCE(hrtimer_get_expires_tv64(timer) < 0);
--
2.34.1
Powered by blists - more mailing lists