Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F157071807
D27571.id81170.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
14 KB
Referenced Files
None
Subscribers
None
D27571.id81170.diff
View Options
diff --git a/include/time.h b/include/time.h
--- a/include/time.h
+++ b/include/time.h
@@ -117,6 +117,7 @@
#define CLOCK_SECOND 13 /* FreeBSD-specific. */
#define CLOCK_THREAD_CPUTIME_ID 14
#define CLOCK_PROCESS_CPUTIME_ID 15
+/* CLOCK_ADJUST_REALTIME 16 is FreeBSD-private */
#endif /* !defined(CLOCK_MONOTONIC) && __POSIX_VISIBLE >= 200112 */
#if __BSD_VISIBLE
diff --git a/lib/libc/gen/Makefile.inc b/lib/libc/gen/Makefile.inc
--- a/lib/libc/gen/Makefile.inc
+++ b/lib/libc/gen/Makefile.inc
@@ -28,6 +28,7 @@
check_utility_compat.c \
clock.c \
clock_getcpuclockid.c \
+ clock_settime_adjust.c \
closedir.c \
confstr.c \
crypt.c \
diff --git a/lib/libc/gen/Symbol.map b/lib/libc/gen/Symbol.map
--- a/lib/libc/gen/Symbol.map
+++ b/lib/libc/gen/Symbol.map
@@ -422,6 +422,7 @@
};
FBSD_1.6 {
+ clock_settime_adjust;
getlogin_r;
memalign;
scandir_b;
diff --git a/lib/libc/gen/clock_settime_adjust.c b/lib/libc/gen/clock_settime_adjust.c
new file mode 100644
--- /dev/null
+++ b/lib/libc/gen/clock_settime_adjust.c
@@ -0,0 +1,49 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2020 The FreeBSD Foundation
+ *
+ * Portions of this software were developed by Konstantin Belousov
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/syscall.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+int
+clock_settime_adjust(const struct timespec *adj, struct timespec *new)
+{
+ struct timespec adj1;
+ int error;
+
+ adj1 = *adj;
+ error == syscall(SYS_clock_settime, CLOCK_ADJUST_REALTIME, &adj1);
+ if (error == 0 && new != NULL)
+ *new = adj1;
+ return (error);
+}
diff --git a/sys/compat/freebsd32/freebsd32_misc.c b/sys/compat/freebsd32/freebsd32_misc.c
--- a/sys/compat/freebsd32/freebsd32_misc.c
+++ b/sys/compat/freebsd32/freebsd32_misc.c
@@ -2919,17 +2919,25 @@
freebsd32_clock_settime(struct thread *td,
struct freebsd32_clock_settime_args *uap)
{
- struct timespec ats;
- struct timespec32 ats32;
+ struct timespec ats, ots;
+ struct timespec32 ats32, ots32;
int error;
error = copyin(uap->tp, &ats32, sizeof(ats32));
- if (error)
+ if (error != 0)
return (error);
CP(ats32, ats, tv_sec);
CP(ats32, ats, tv_nsec);
- return (kern_clock_settime(td, uap->clock_id, &ats));
+ error = kern_clock_settime(td, uap->clock_id, &ats, &ots);
+
+ if (error == 0 && uap->clock_id == CLOCK_ADJUST_REALTIME) {
+ CP(ots, ots32, tv_sec);
+ CP(ots, ots32, tv_nsec);
+ error = copyout(&ots32, __DECONST(void *, uap->tp),
+ sizeof(ots32));
+ }
+ return (error);
}
int
diff --git a/sys/compat/linux/linux_time.c b/sys/compat/linux/linux_time.c
--- a/sys/compat/linux/linux_time.c
+++ b/sys/compat/linux/linux_time.c
@@ -422,7 +422,7 @@
return (error);
}
- error = kern_clock_settime(td, nwhich, &ts);
+ error = kern_clock_settime(td, nwhich, &ts, NULL);
if (error != 0)
LIN_SDT_PROBE1(time, linux_clock_settime, settime_error, error);
diff --git a/sys/dev/hyperv/utilities/vmbus_timesync.c b/sys/dev/hyperv/utilities/vmbus_timesync.c
--- a/sys/dev/hyperv/utilities/vmbus_timesync.c
+++ b/sys/dev/hyperv/utilities/vmbus_timesync.c
@@ -132,7 +132,7 @@
}
hv_ts.tv_sec = hv_ns / NANOSEC;
hv_ts.tv_nsec = hv_ns % NANOSEC;
- kern_clock_settime(curthread, CLOCK_REALTIME, &hv_ts);
+ kern_clock_settime(curthread, CLOCK_REALTIME, &hv_ts, NULL);
/* Done! */
return;
}
@@ -164,7 +164,8 @@
}
hv_ts.tv_sec = hv_ns / NANOSEC;
hv_ts.tv_nsec = hv_ns % NANOSEC;
- kern_clock_settime(curthread, CLOCK_REALTIME, &hv_ts);
+ kern_clock_settime(curthread, CLOCK_REALTIME, &hv_ts,
+ NULL);
}
/* Done */
return;
diff --git a/sys/kern/kern_tc.c b/sys/kern/kern_tc.c
--- a/sys/kern/kern_tc.c
+++ b/sys/kern/kern_tc.c
@@ -1245,7 +1245,7 @@
* when we booted.
*/
void
-tc_setclock(struct timespec *ts)
+tc_setclock(struct timespec *ts, bool abs)
{
struct timespec tbef, taft;
struct bintime bt, bt2;
@@ -1254,10 +1254,14 @@
nanotime(&tbef);
mtx_lock_spin(&tc_setclock_mtx);
cpu_tick_calibrate(1);
- binuptime(&bt2);
- bintime_sub(&bt, &bt2);
+ if (abs) {
+ binuptime(&bt2);
+ bintime_sub(&bt, &bt2);
+ }
- /* XXX fiddle all the little crinkly bits around the fiords... */
+ /*
+ * Fiddle all the little crinkly bits around the fiords...
+ */
tc_windup(&bt);
mtx_unlock_spin(&tc_setclock_mtx);
diff --git a/sys/kern/kern_time.c b/sys/kern/kern_time.c
--- a/sys/kern/kern_time.c
+++ b/sys/kern/kern_time.c
@@ -86,6 +86,8 @@
*/
static int settime(struct thread *, struct timeval *);
+static int steplimit(struct thread *td, struct timeval *tv, bool abs);
+static void steptime(struct timeval *, bool abs);
static void timevalfix(struct timeval *);
static int user_clock_nanosleep(struct thread *td, clockid_t clock_id,
int flags, const struct timespec *ua_rqtp,
@@ -113,18 +115,53 @@
#define CLOCK_CALL(clock, call, arglist) \
((*posix_clocks[clock].call) arglist)
+static SYSCTL_NODE(_kern, OID_AUTO, settime, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
+ "Controls for settime");
+
+static int allow_insane_settime = 0;
+SYSCTL_INT(_kern_settime, OID_AUTO, allow_insane_settime, CTLFLAG_RWTUN,
+ &allow_insane_settime, 0,
+ "do not perform possibly restrictive checks on settime(2) args");
+static u_long insane_settime_val = 8000;
+SYSCTL_ULONG(_kern_settime, OID_AUTO, insane_settime_val, CTLFLAG_RWTUN,
+ &insane_settime_val, 0,
+ "insane years for clock_settime(2)");
+static int secure_steplimit = 1;
+SYSCTL_INT(_kern_settime, OID_AUTO, secure_steplimit, CTLFLAG_RWTUN,
+ &secure_steplimit, 0,
+ "limit for step on securelevel > 1, in seconds");
+/*
+ * Default limit it set to 12h to allow adjustment to fix local-zone RTC
+ * without requiring tuning.
+ */
+static int insane_adjust_limit = 12 * 3600;
+SYSCTL_INT(_kern_settime, OID_AUTO, insane_adjust_limit, CTLFLAG_RWTUN,
+ &insane_adjust_limit, 0,
+ "insane seconds for CLOCK_ADJUST_REALTIME");
+
SYSINIT(posix_timer, SI_SUB_P1003_1B, SI_ORDER_FIRST+4, itimer_start, NULL);
+static struct timeval maxtime, laststep;
+static struct mtx sl_lock;
+MTX_SYSINIT(sl_lock, &sl_lock, "sll", MTX_DEF);
+
static int
-settime(struct thread *td, struct timeval *tv)
+steplimit(struct thread *td, struct timeval *tv, bool abs)
{
struct timeval delta, tv1, tv2;
- static struct timeval maxtime, laststep;
- struct timespec ts;
+ int error, sl;
+
+ if (securelevel_gt(td->td_ucred, 1) == 0)
+ return (0);
+ error = 0;
+ mtx_lock(&sl_lock);
microtime(&tv1);
delta = *tv;
- timevalsub(&delta, &tv1);
+ if (abs)
+ timevalsub(&delta, &tv1);
+
+ sl = secure_steplimit;
/*
* If the system is secure, we do not allow the time to be
@@ -132,40 +169,81 @@
* time we have yet seen. The worst a miscreant can do in
* this circumstance is "freeze" time. He couldn't go
* back to the past.
- *
- * We similarly do not allow the clock to be stepped more
- * than one second, nor more than once per second. This allows
- * a miscreant to make the clock march double-time, but no worse.
*/
- if (securelevel_gt(td->td_ucred, 1) != 0) {
- if (delta.tv_sec < 0 || delta.tv_usec < 0) {
- /*
- * Update maxtime to latest time we've seen.
- */
- if (tv1.tv_sec > maxtime.tv_sec)
- maxtime = tv1;
+ if (delta.tv_sec < 0 || delta.tv_usec < 0) {
+ /*
+ * Update maxtime to latest time we've seen.
+ */
+ if (tv1.tv_sec > maxtime.tv_sec)
+ maxtime = tv1;
+ if (abs) {
tv2 = *tv;
timevalsub(&tv2, &maxtime);
- if (tv2.tv_sec < -1) {
- tv->tv_sec = maxtime.tv_sec - 1;
- printf("Time adjustment clamped to -1 second\n");
+ if (tv2.tv_sec < -sl) {
+ tv->tv_sec = maxtime.tv_sec - sl;
+ if (bootverbose) {
+ printf(
+ "Time adjustment clamped to -%d second\n",
+ sl);
+ }
}
} else {
- if (tv1.tv_sec == laststep.tv_sec)
- return (EPERM);
- if (delta.tv_sec > 1) {
- tv->tv_sec = tv1.tv_sec + 1;
- printf("Time adjustment clamped to +1 second\n");
+ if (delta.tv_sec < -sl) {
+ tv->tv_sec = - sl;
+ if (bootverbose) {
+ printf(
+ "Time adjustment clamped to -%d second\n",
+ sl);
+ }
}
- laststep = *tv;
}
+ goto out;
}
+ /*
+ * We similarly do not allow the clock to be stepped more
+ * than one second, nor more than once per second. This allows
+ * a miscreant to make the clock march double-time, but no worse.
+ */
+ if (tv1.tv_sec == laststep.tv_sec) {
+ error = EPERM;
+ goto out;
+ }
+ if (delta.tv_sec > sl) {
+ tv->tv_sec = abs ? tv1.tv_sec + sl : sl;
+ printf("Time adjustment clamped to +%d second\n", sl);
+ }
+ if (abs) {
+ laststep = *tv;
+ } else {
+ timevaladd(&tv1, &delta);
+ laststep = tv1;
+ }
+out:
+ mtx_unlock(&sl_lock);
+ return (error);
+}
+
+static int
+settime(struct thread *td, struct timeval *tv)
+{
+ int error;
+
+ error = steplimit(td, tv, true);
+ if (error == 0)
+ steptime(tv, true);
+ return (error);
+}
+
+static void
+steptime(struct timeval *tv, bool abs)
+{
+ struct timespec ts;
+
ts.tv_sec = tv->tv_sec;
ts.tv_nsec = tv->tv_usec * 1000;
- tc_setclock(&ts);
+ tc_setclock(&ts, abs);
resettodr();
- return (0);
}
#ifndef _SYS_SYSPROTO_H_
@@ -384,39 +462,56 @@
int
sys_clock_settime(struct thread *td, struct clock_settime_args *uap)
{
- struct timespec ats;
+ struct timespec ats, ots;
int error;
if ((error = copyin(uap->tp, &ats, sizeof(ats))) != 0)
return (error);
- return (kern_clock_settime(td, uap->clock_id, &ats));
+ error = kern_clock_settime(td, uap->clock_id, &ats, &ots);
+ if (error == 0 && uap->clock_id == CLOCK_ADJUST_REALTIME)
+ error = copyout(&ots, __DECONST(void *, uap->tp), sizeof(ots));
+ return (error);
}
-static int allow_insane_settime = 0;
-SYSCTL_INT(_debug, OID_AUTO, allow_insane_settime, CTLFLAG_RWTUN,
- &allow_insane_settime, 0,
- "do not perform possibly restrictive checks on settime(2) args");
-
int
-kern_clock_settime(struct thread *td, clockid_t clock_id, struct timespec *ats)
+kern_clock_settime(struct thread *td, clockid_t clock_id, struct timespec *ats,
+ struct timespec *ots)
{
struct timeval atv;
int error;
if ((error = priv_check(td, PRIV_CLOCK_SETTIME)) != 0)
return (error);
- if (clock_id != CLOCK_REALTIME)
- return (EINVAL);
- if (ats->tv_nsec < 0 || ats->tv_nsec >= 1000000000 ||
- ats->tv_sec < 0)
- return (EINVAL);
- if (!allow_insane_settime &&
- (ats->tv_sec > 8000ULL * 365 * 24 * 60 * 60 ||
- ats->tv_sec < utc_offset()))
- return (EINVAL);
- /* XXX Don't convert nsec->usec and back */
- TIMESPEC_TO_TIMEVAL(&atv, ats);
- error = settime(td, &atv);
+ error = EINVAL;
+ switch (clock_id) {
+ case CLOCK_ADJUST_REALTIME:
+ if (ats->tv_nsec < 0 || ats->tv_nsec >= 1000000000)
+ break;
+ if (!allow_insane_settime &&
+ (ats->tv_sec > insane_adjust_limit ||
+ ats->tv_sec < -insane_adjust_limit))
+ break;
+ TIMESPEC_TO_TIMEVAL(&atv, ats);
+ error = steplimit(td, &atv, false);
+ if (error != 0)
+ break;
+ steptime(&atv, false);
+ if (ots != NULL)
+ TIMEVAL_TO_TIMESPEC(&atv, ots);
+ break;
+ case CLOCK_REALTIME:
+ if (ats->tv_nsec < 0 || ats->tv_nsec >= 1000000000 ||
+ ats->tv_sec < 0)
+ break;
+ if (!allow_insane_settime &&
+ (ats->tv_sec > insane_settime_val * 365ULL * 24 * 60 * 60 ||
+ ats->tv_sec < utc_offset()))
+ break;
+ /* XXX Don't convert nsec->usec and back */
+ TIMESPEC_TO_TIMEVAL(&atv, ats);
+ error = settime(td, &atv);
+ break;
+ }
return (error);
}
diff --git a/sys/kern/subr_rtc.c b/sys/kern/subr_rtc.c
--- a/sys/kern/subr_rtc.c
+++ b/sys/kern/subr_rtc.c
@@ -361,7 +361,7 @@
}
if (ts.tv_sec >= 0) {
- tc_setclock(&ts);
+ tc_setclock(&ts, true);
#ifdef FFCLOCK
ffclock_reset_clock(&ts);
#endif
diff --git a/sys/sys/syscallsubr.h b/sys/sys/syscallsubr.h
--- a/sys/sys/syscallsubr.h
+++ b/sys/sys/syscallsubr.h
@@ -105,7 +105,7 @@
int kern_clock_nanosleep(struct thread *td, clockid_t clock_id, int flags,
const struct timespec *rqtp, struct timespec *rmtp);
int kern_clock_settime(struct thread *td, clockid_t clock_id,
- struct timespec *ats);
+ struct timespec *ats, struct timespec *ots);
void kern_thread_cputime(struct thread *targettd, struct timespec *ats);
void kern_process_cputime(struct proc *targetp, struct timespec *ats);
int kern_close_range(struct thread *td, u_int lowfd, u_int highfd);
diff --git a/sys/sys/time.h b/sys/sys/time.h
--- a/sys/sys/time.h
+++ b/sys/sys/time.h
@@ -480,6 +480,7 @@
#define CLOCK_SECOND 13 /* FreeBSD-specific. */
#define CLOCK_THREAD_CPUTIME_ID 14
#define CLOCK_PROCESS_CPUTIME_ID 15
+#define CLOCK_ADJUST_REALTIME 16 /* FreeBSD-specific. */
#endif
#ifndef TIMER_ABSTIME
@@ -613,6 +614,7 @@
#if __BSD_VISIBLE
int adjtime(const struct timeval *, struct timeval *);
int clock_getcpuclockid2(id_t, int, clockid_t *);
+int clock_settime_adjust(const struct timespec *, struct timespec *);
int futimes(int, const struct timeval *);
int futimesat(int, const char *, const struct timeval [2]);
int lutimes(const char *, const struct timeval *);
diff --git a/sys/sys/timetc.h b/sys/sys/timetc.h
--- a/sys/sys/timetc.h
+++ b/sys/sys/timetc.h
@@ -88,7 +88,7 @@
u_int64_t tc_getfrequency(void);
void tc_init(struct timecounter *tc);
-void tc_setclock(struct timespec *ts);
+void tc_setclock(struct timespec *ts, bool abs);
void tc_ticktock(int cnt);
void cpu_tick_calibration(void);
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Tue, May 19, 6:52 AM (11 h, 55 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
33298120
Default Alt Text
D27571.id81170.diff (14 KB)
Attached To
Mode
D27571: CLOCK_ADJUST_REALTIME
Attached
Detach File
Event Timeline
Log In to Comment