timekeeping: Force upper bound for setting CLOCK_REALTIME
authorThomas Gleixner <tglx@linutronix.de>
Sat, 23 Mar 2019 10:36:19 +0000 (11:36 +0100)
committerThomas Gleixner <tglx@linutronix.de>
Thu, 28 Mar 2019 12:41:06 +0000 (13:41 +0100)
Several people reported testing failures after setting CLOCK_REALTIME close
to the limits of the kernel internal representation in nanoseconds,
i.e. year 2262.

The failures are exposed in subsequent operations, i.e. when arming timers
or when the advancing CLOCK_MONOTONIC makes the calculation of
CLOCK_REALTIME overflow into negative space.

Now people start to paper over the underlying problem by clamping
calculations to the valid range, but that's just wrong because such
workarounds will prevent detection of real issues as well.

It is reasonable to force an upper bound for the various methods of setting
CLOCK_REALTIME. Year 2262 is the absolute upper bound. Assume a maximum
uptime of 30 years which is plenty enough even for esoteric embedded
systems. That results in an upper bound of year 2232 for setting the time.

Once that limit is reached in reality this limit is only a small part of
the problem space. But until then this stops people from trying to paper
over the problem at the wrong places.

Reported-by: Xiongfeng Wang <wangxiongfeng2@huawei.com>
Reported-by: Hongbo Yao <yaohongbo@huawei.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: John Stultz <john.stultz@linaro.org>
Cc: Stephen Boyd <sboyd@kernel.org>
Cc: Miroslav Lichvar <mlichvar@redhat.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Richard Cochran <richardcochran@gmail.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: https://lkml.kernel.org/r/alpine.DEB.2.21.1903231125480.2157@nanos.tec.linutronix.de
include/linux/time64.h
kernel/time/time.c
kernel/time/timekeeping.c

index f38d382ffec1363ca9dbf4e78d9ee6db968c443e..a620ee610b9f3e07ae76fc4e8d28563258f5c0df 100644 (file)
@@ -33,6 +33,17 @@ struct itimerspec64 {
 #define KTIME_MAX                      ((s64)~((u64)1 << 63))
 #define KTIME_SEC_MAX                  (KTIME_MAX / NSEC_PER_SEC)
 
+/*
+ * Limits for settimeofday():
+ *
+ * To prevent setting the time close to the wraparound point time setting
+ * is limited so a reasonable uptime can be accomodated. Uptime of 30 years
+ * should be really sufficient, which means the cutoff is 2232. At that
+ * point the cutoff is just a small part of the larger problem.
+ */
+#define TIME_UPTIME_SEC_MAX            (30LL * 365 * 24 *3600)
+#define TIME_SETTOD_SEC_MAX            (KTIME_SEC_MAX - TIME_UPTIME_SEC_MAX)
+
 static inline int timespec64_equal(const struct timespec64 *a,
                                   const struct timespec64 *b)
 {
@@ -100,6 +111,16 @@ static inline bool timespec64_valid_strict(const struct timespec64 *ts)
        return true;
 }
 
+static inline bool timespec64_valid_settod(const struct timespec64 *ts)
+{
+       if (!timespec64_valid(ts))
+               return false;
+       /* Disallow values which cause overflow issues vs. CLOCK_REALTIME */
+       if ((unsigned long long)ts->tv_sec >= TIME_SETTOD_SEC_MAX)
+               return false;
+       return true;
+}
+
 /**
  * timespec64_to_ns - Convert timespec64 to nanoseconds
  * @ts:                pointer to the timespec64 variable to be converted
index c3f756f8534bba606b1fcee479bfcb0e05829502..86656bbac232e659d6ba4f38242bc774b3a6cbde 100644 (file)
@@ -171,7 +171,7 @@ int do_sys_settimeofday64(const struct timespec64 *tv, const struct timezone *tz
        static int firsttime = 1;
        int error = 0;
 
-       if (tv && !timespec64_valid(tv))
+       if (tv && !timespec64_valid_settod(tv))
                return -EINVAL;
 
        error = security_settime64(tv, tz);
index 540145da33dac504b17691c12a509139546f1992..5716e28bfa3cc7e164ce8dbda857141921a00e90 100644 (file)
@@ -1221,7 +1221,7 @@ int do_settimeofday64(const struct timespec64 *ts)
        unsigned long flags;
        int ret = 0;
 
-       if (!timespec64_valid_strict(ts))
+       if (!timespec64_valid_settod(ts))
                return -EINVAL;
 
        raw_spin_lock_irqsave(&timekeeper_lock, flags);
@@ -1278,7 +1278,7 @@ static int timekeeping_inject_offset(const struct timespec64 *ts)
        /* Make sure the proposed value is valid */
        tmp = timespec64_add(tk_xtime(tk), *ts);
        if (timespec64_compare(&tk->wall_to_monotonic, ts) > 0 ||
-           !timespec64_valid_strict(&tmp)) {
+           !timespec64_valid_settod(&tmp)) {
                ret = -EINVAL;
                goto error;
        }
@@ -1527,7 +1527,7 @@ void __init timekeeping_init(void)
        unsigned long flags;
 
        read_persistent_wall_and_boot_offset(&wall_time, &boot_offset);
-       if (timespec64_valid_strict(&wall_time) &&
+       if (timespec64_valid_settod(&wall_time) &&
            timespec64_to_ns(&wall_time) > 0) {
                persistent_clock_exists = true;
        } else if (timespec64_to_ns(&wall_time) != 0) {