summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorcheloha <cheloha@openbsd.org>2019-01-31 05:00:18 +0000
committercheloha <cheloha@openbsd.org>2019-01-31 05:00:18 +0000
commite12a049bd4bbd1e8315c373a739e08972ed6dd1d (patch)
tree89b2f2100ef5c9651266cbd1dec8063e3f74e8ed
parentuse "sc" as the name of the softc variable in the ioctl code too. (diff)
downloadwireguard-openbsd-e12a049bd4bbd1e8315c373a739e08972ed6dd1d.tar.xz
wireguard-openbsd-e12a049bd4bbd1e8315c373a739e08972ed6dd1d.zip
tc_setclock: Don't rewind the system uptime during resume/unhibernate.
When we come back from suspend/hibernate the BIOS/firmware/whatever can hand us *any* TOD, so we need to check that the given TOD doesn't set our boot offset backwards, breaking the monotonicity of e.g. CLOCK_MONOTONIC. This is trivial to do from the BIOS on most PCs before unhibernating. There might be other ways it can happen, accidentally or otherwise. This is a bit messy but it can be made prettier later with a "bintimecmp" macro or something like that. Problem confirmed by jmatthew@. "you are very likely right" deraadt@
-rw-r--r--sys/kern/kern_tc.c17
1 files changed, 16 insertions, 1 deletions
diff --git a/sys/kern/kern_tc.c b/sys/kern/kern_tc.c
index 5fde04474a7..607d06fbeee 100644
--- a/sys/kern/kern_tc.c
+++ b/sys/kern/kern_tc.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: kern_tc.c,v 1.36 2019/01/20 01:13:03 cheloha Exp $ */
+/* $OpenBSD: kern_tc.c,v 1.37 2019/01/31 05:00:18 cheloha Exp $ */
/*
* Copyright (c) 2000 Poul-Henning Kamp <phk@FreeBSD.org>
@@ -382,6 +382,7 @@ void
tc_setclock(const struct timespec *ts)
{
struct bintime bt, bt2;
+ struct timespec earlier;
static int first = 1;
#ifndef SMALL_KERNEL
long long adj_ticks;
@@ -402,6 +403,20 @@ tc_setclock(const struct timespec *ts)
mtx_enter(&timecounter_mtx);
timespec2bintime(ts, &bt);
bintime_sub(&bt, &timehands->th_boottime);
+
+ /*
+ * Don't rewind the offset.
+ */
+ if (bt.sec < timehands->th_offset.sec ||
+ (bt.sec == timehands->th_offset.sec &&
+ bt.frac < timehands->th_offset.frac)) {
+ mtx_leave(&timecounter_mtx);
+ bintime2timespec(&bt, &earlier);
+ printf("%s: cannot rewind uptime to %lld.%09ld\n",
+ __func__, (long long)earlier.tv_sec, earlier.tv_nsec);
+ return;
+ }
+
bt2 = timehands->th_offset;
timehands->th_offset = bt;