aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/time
diff options
context:
space:
mode:
authorJohn Stultz <john.stultz@linaro.org>2012-08-08 15:36:20 -0400
committerThomas Gleixner <tglx@linutronix.de>2012-08-15 09:54:01 -0400
commit4e8b14526ca7fb046a81c94002c1c43b6fdf0e9b (patch)
treed0681e8e99fe2fbae1bfa574a8a68320ae5c7c1f /kernel/time
parent3bf671af14d591ede9251acb0085e8017f3705e7 (diff)
time: Improve sanity checking of timekeeping inputs
Unexpected behavior could occur if the time is set to a value large enough to overflow a 64bit ktime_t (which is something larger then the year 2262). Also unexpected behavior could occur if large negative offsets are injected via adjtimex. So this patch improves the sanity check timekeeping inputs by improving the timespec_valid() check, and then makes better use of timespec_valid() to make sure we don't set the time to an invalid negative value or one that overflows ktime_t. Note: This does not protect from setting the time close to overflowing ktime_t and then letting natural accumulation cause the overflow. Reported-by: CAI Qian <caiqian@redhat.com> Reported-by: Sasha Levin <levinsasha928@gmail.com> Signed-off-by: John Stultz <john.stultz@linaro.org> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: Prarit Bhargava <prarit@redhat.com> Cc: Zhouping Liu <zliu@redhat.com> Cc: Ingo Molnar <mingo@kernel.org> Cc: stable@vger.kernel.org Link: http://lkml.kernel.org/r/1344454580-17031-1-git-send-email-john.stultz@linaro.org Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'kernel/time')
-rw-r--r--kernel/time/timekeeping.c26
1 files changed, 24 insertions, 2 deletions
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index e16af197a2bc..898bef066a44 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -427,7 +427,7 @@ int do_settimeofday(const struct timespec *tv)
427 struct timespec ts_delta, xt; 427 struct timespec ts_delta, xt;
428 unsigned long flags; 428 unsigned long flags;
429 429
430 if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC) 430 if (!timespec_valid(tv))
431 return -EINVAL; 431 return -EINVAL;
432 432
433 write_seqlock_irqsave(&tk->lock, flags); 433 write_seqlock_irqsave(&tk->lock, flags);
@@ -463,6 +463,8 @@ int timekeeping_inject_offset(struct timespec *ts)
463{ 463{
464 struct timekeeper *tk = &timekeeper; 464 struct timekeeper *tk = &timekeeper;
465 unsigned long flags; 465 unsigned long flags;
466 struct timespec tmp;
467 int ret = 0;
466 468
467 if ((unsigned long)ts->tv_nsec >= NSEC_PER_SEC) 469 if ((unsigned long)ts->tv_nsec >= NSEC_PER_SEC)
468 return -EINVAL; 470 return -EINVAL;
@@ -471,10 +473,17 @@ int timekeeping_inject_offset(struct timespec *ts)
471 473
472 timekeeping_forward_now(tk); 474 timekeeping_forward_now(tk);
473 475
476 /* Make sure the proposed value is valid */
477 tmp = timespec_add(tk_xtime(tk), *ts);
478 if (!timespec_valid(&tmp)) {
479 ret = -EINVAL;
480 goto error;
481 }
474 482
475 tk_xtime_add(tk, ts); 483 tk_xtime_add(tk, ts);
476 tk_set_wall_to_mono(tk, timespec_sub(tk->wall_to_monotonic, *ts)); 484 tk_set_wall_to_mono(tk, timespec_sub(tk->wall_to_monotonic, *ts));
477 485
486error: /* even if we error out, we forwarded the time, so call update */
478 timekeeping_update(tk, true); 487 timekeeping_update(tk, true);
479 488
480 write_sequnlock_irqrestore(&tk->lock, flags); 489 write_sequnlock_irqrestore(&tk->lock, flags);
@@ -482,7 +491,7 @@ int timekeeping_inject_offset(struct timespec *ts)
482 /* signal hrtimers about time change */ 491 /* signal hrtimers about time change */
483 clock_was_set(); 492 clock_was_set();
484 493
485 return 0; 494 return ret;
486} 495}
487EXPORT_SYMBOL(timekeeping_inject_offset); 496EXPORT_SYMBOL(timekeeping_inject_offset);
488 497
@@ -649,7 +658,20 @@ void __init timekeeping_init(void)
649 struct timespec now, boot, tmp; 658 struct timespec now, boot, tmp;
650 659
651 read_persistent_clock(&now); 660 read_persistent_clock(&now);
661 if (!timespec_valid(&now)) {
662 pr_warn("WARNING: Persistent clock returned invalid value!\n"
663 " Check your CMOS/BIOS settings.\n");
664 now.tv_sec = 0;
665 now.tv_nsec = 0;
666 }
667
652 read_boot_clock(&boot); 668 read_boot_clock(&boot);
669 if (!timespec_valid(&boot)) {
670 pr_warn("WARNING: Boot clock returned invalid value!\n"
671 " Check your CMOS/BIOS settings.\n");
672 boot.tv_sec = 0;
673 boot.tv_nsec = 0;
674 }
653 675
654 seqlock_init(&tk->lock); 676 seqlock_init(&tk->lock);
655 677