| From 4e8c11b6b3f0b6a283e898344f154641eda94266 Mon Sep 17 00:00:00 2001 |
| From: Yu Liao <liaoyu15@huawei.com> |
| Date: Mon, 13 Dec 2021 21:57:27 +0800 |
| Subject: timekeeping: Really make sure wall_to_monotonic isn't positive |
| |
| From: Yu Liao <liaoyu15@huawei.com> |
| |
| commit 4e8c11b6b3f0b6a283e898344f154641eda94266 upstream. |
| |
| Even after commit e1d7ba873555 ("time: Always make sure wall_to_monotonic |
| isn't positive") it is still possible to make wall_to_monotonic positive |
| by running the following code: |
| |
| int main(void) |
| { |
| struct timespec time; |
| |
| clock_gettime(CLOCK_MONOTONIC, &time); |
| time.tv_nsec = 0; |
| clock_settime(CLOCK_REALTIME, &time); |
| return 0; |
| } |
| |
| The reason is that the second parameter of timespec64_compare(), ts_delta, |
| may be unnormalized because the delta is calculated with an open coded |
| substraction which causes the comparison of tv_sec to yield the wrong |
| result: |
| |
| wall_to_monotonic = { .tv_sec = -10, .tv_nsec = 900000000 } |
| ts_delta = { .tv_sec = -9, .tv_nsec = -900000000 } |
| |
| That makes timespec64_compare() claim that wall_to_monotonic < ts_delta, |
| but actually the result should be wall_to_monotonic > ts_delta. |
| |
| After normalization, the result of timespec64_compare() is correct because |
| the tv_sec comparison is not longer misleading: |
| |
| wall_to_monotonic = { .tv_sec = -10, .tv_nsec = 900000000 } |
| ts_delta = { .tv_sec = -10, .tv_nsec = 100000000 } |
| |
| Use timespec64_sub() to ensure that ts_delta is normalized, which fixes the |
| issue. |
| |
| Fixes: e1d7ba873555 ("time: Always make sure wall_to_monotonic isn't positive") |
| Signed-off-by: Yu Liao <liaoyu15@huawei.com> |
| Signed-off-by: Thomas Gleixner <tglx@linutronix.de> |
| Cc: stable@vger.kernel.org |
| Link: https://lore.kernel.org/r/20211213135727.1656662-1-liaoyu15@huawei.com |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| kernel/time/timekeeping.c | 3 +-- |
| 1 file changed, 1 insertion(+), 2 deletions(-) |
| |
| --- a/kernel/time/timekeeping.c |
| +++ b/kernel/time/timekeeping.c |
| @@ -1306,8 +1306,7 @@ int do_settimeofday64(const struct times |
| timekeeping_forward_now(tk); |
| |
| xt = tk_xtime(tk); |
| - ts_delta.tv_sec = ts->tv_sec - xt.tv_sec; |
| - ts_delta.tv_nsec = ts->tv_nsec - xt.tv_nsec; |
| + ts_delta = timespec64_sub(*ts, xt); |
| |
| if (timespec64_compare(&tk->wall_to_monotonic, &ts_delta) > 0) { |
| ret = -EINVAL; |