Skip to content

Commit 7a8e61f

Browse files
committed
timekeeping: Force upper bound for setting CLOCK_REALTIME
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 <[email protected]> Reported-by: Hongbo Yao <[email protected]> Signed-off-by: Thomas Gleixner <[email protected]> Cc: John Stultz <[email protected]> Cc: Stephen Boyd <[email protected]> Cc: Miroslav Lichvar <[email protected]> Cc: Arnd Bergmann <[email protected]> Cc: Richard Cochran <[email protected]> Cc: Peter Zijlstra <[email protected]> Link: https://lkml.kernel.org/r/[email protected]
1 parent f28d3d5 commit 7a8e61f

File tree

3 files changed

+25
-4
lines changed

3 files changed

+25
-4
lines changed

include/linux/time64.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,17 @@ struct itimerspec64 {
3333
#define KTIME_MAX ((s64)~((u64)1 << 63))
3434
#define KTIME_SEC_MAX (KTIME_MAX / NSEC_PER_SEC)
3535

36+
/*
37+
* Limits for settimeofday():
38+
*
39+
* To prevent setting the time close to the wraparound point time setting
40+
* is limited so a reasonable uptime can be accomodated. Uptime of 30 years
41+
* should be really sufficient, which means the cutoff is 2232. At that
42+
* point the cutoff is just a small part of the larger problem.
43+
*/
44+
#define TIME_UPTIME_SEC_MAX (30LL * 365 * 24 *3600)
45+
#define TIME_SETTOD_SEC_MAX (KTIME_SEC_MAX - TIME_UPTIME_SEC_MAX)
46+
3647
static inline int timespec64_equal(const struct timespec64 *a,
3748
const struct timespec64 *b)
3849
{
@@ -100,6 +111,16 @@ static inline bool timespec64_valid_strict(const struct timespec64 *ts)
100111
return true;
101112
}
102113

114+
static inline bool timespec64_valid_settod(const struct timespec64 *ts)
115+
{
116+
if (!timespec64_valid(ts))
117+
return false;
118+
/* Disallow values which cause overflow issues vs. CLOCK_REALTIME */
119+
if ((unsigned long long)ts->tv_sec >= TIME_SETTOD_SEC_MAX)
120+
return false;
121+
return true;
122+
}
123+
103124
/**
104125
* timespec64_to_ns - Convert timespec64 to nanoseconds
105126
* @ts: pointer to the timespec64 variable to be converted

kernel/time/time.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ int do_sys_settimeofday64(const struct timespec64 *tv, const struct timezone *tz
171171
static int firsttime = 1;
172172
int error = 0;
173173

174-
if (tv && !timespec64_valid(tv))
174+
if (tv && !timespec64_valid_settod(tv))
175175
return -EINVAL;
176176

177177
error = security_settime64(tv, tz);

kernel/time/timekeeping.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1221,7 +1221,7 @@ int do_settimeofday64(const struct timespec64 *ts)
12211221
unsigned long flags;
12221222
int ret = 0;
12231223

1224-
if (!timespec64_valid_strict(ts))
1224+
if (!timespec64_valid_settod(ts))
12251225
return -EINVAL;
12261226

12271227
raw_spin_lock_irqsave(&timekeeper_lock, flags);
@@ -1278,7 +1278,7 @@ static int timekeeping_inject_offset(const struct timespec64 *ts)
12781278
/* Make sure the proposed value is valid */
12791279
tmp = timespec64_add(tk_xtime(tk), *ts);
12801280
if (timespec64_compare(&tk->wall_to_monotonic, ts) > 0 ||
1281-
!timespec64_valid_strict(&tmp)) {
1281+
!timespec64_valid_settod(&tmp)) {
12821282
ret = -EINVAL;
12831283
goto error;
12841284
}
@@ -1527,7 +1527,7 @@ void __init timekeeping_init(void)
15271527
unsigned long flags;
15281528

15291529
read_persistent_wall_and_boot_offset(&wall_time, &boot_offset);
1530-
if (timespec64_valid_strict(&wall_time) &&
1530+
if (timespec64_valid_settod(&wall_time) &&
15311531
timespec64_to_ns(&wall_time) > 0) {
15321532
persistent_clock_exists = true;
15331533
} else if (timespec64_to_ns(&wall_time) != 0) {

0 commit comments

Comments
 (0)