diff --git a/cmdmon.c b/cmdmon.c index 9690989..6a87376 100644 --- a/cmdmon.c +++ b/cmdmon.c @@ -1555,8 +1555,11 @@ handle_manual_delete(CMD_Request *rx_message, CMD_Reply *tx_message) static void handle_make_step(CMD_Request *rx_message, CMD_Reply *tx_message) { - LCL_MakeStep(); - tx_message->status = htons(STT_SUCCESS); + if (!LCL_MakeStep()) { + tx_message->status = htons(STT_FAILED); + } else { + tx_message->status = htons(STT_SUCCESS); + } } /* ================================================== */ diff --git a/local.c b/local.c index e301802..f81c35d 100644 --- a/local.c +++ b/local.c @@ -416,6 +416,19 @@ clamp_freq(double freq) /* ================================================== */ +static int +check_offset(struct timeval *now, double offset) +{ + /* Check if the time will be still sane with accumulated offset */ + if (UTI_IsTimeOffsetSane(now, -offset)) + return 1; + + LOG(LOGS_WARN, LOGF_Local, "Adjustment of %.1f seconds is invalid", -offset); + return 0; +} + +/* ================================================== */ + /* This involves both setting the absolute frequency with the system-specific driver, as well as calling all notify handlers */ @@ -490,6 +503,9 @@ LCL_AccumulateOffset(double offset, double corr_rate) LCL_ReadRawTime(&raw); LCL_CookTime(&raw, &cooked, NULL); + if (!check_offset(&cooked, offset)) + return; + (*drv_accrue_offset)(offset, corr_rate); /* Dispatch to all handlers */ @@ -498,7 +514,7 @@ LCL_AccumulateOffset(double offset, double corr_rate) /* ================================================== */ -void +int LCL_ApplyStepOffset(double offset) { struct timeval raw, cooked; @@ -509,6 +525,9 @@ LCL_ApplyStepOffset(double offset) LCL_ReadRawTime(&raw); LCL_CookTime(&raw, &cooked, NULL); + if (!check_offset(&raw, offset)) + return 0; + (*drv_apply_step_offset)(offset); /* Reset smoothing on all clock steps */ @@ -516,6 +535,8 @@ LCL_ApplyStepOffset(double offset) /* Dispatch to all handlers */ invoke_parameter_change_handlers(&raw, &cooked, 0.0, offset, LCL_ChangeStep); + + return 1; } /* ================================================== */ @@ -557,6 +578,9 @@ LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate) to the change we are about to make */ LCL_CookTime(&raw, &cooked, NULL); + if (!check_offset(&cooked, doffset)) + return; + old_freq_ppm = current_freq_ppm; /* Work out new absolute frequency. Note that absolute frequencies @@ -629,9 +653,13 @@ LCL_MakeStep(void) LCL_ReadRawTime(&raw); LCL_GetOffsetCorrection(&raw, &correction, NULL); + if (!check_offset(&raw, -correction)) + return 0; + /* Cancel remaining slew and make the step */ LCL_AccumulateOffset(correction, 0.0); - LCL_ApplyStepOffset(-correction); + if (!LCL_ApplyStepOffset(-correction)) + return 0; LOG(LOGS_WARN, LOGF_Local, "System clock was stepped by %.6f seconds", correction); diff --git a/local.h b/local.h index 412b386..0dfdd73 100644 --- a/local.h +++ b/local.h @@ -159,7 +159,7 @@ extern void LCL_AccumulateOffset(double offset, double corr_rate); the system clock is fast on true time, i.e. it needs to be stepped backwards. (Same convention as for AccumulateOffset routine). */ -extern void LCL_ApplyStepOffset(double offset); +extern int LCL_ApplyStepOffset(double offset); /* Routine to invoke notify handlers on an unexpected time jump in system clock */ diff --git a/reference.c b/reference.c index a8492bc..d85b610 100644 --- a/reference.c +++ b/reference.c @@ -1051,8 +1051,8 @@ REF_SetReference(int stratum, maybe_log_offset(our_offset, raw_now.tv_sec); if (step_offset != 0.0) { - LCL_ApplyStepOffset(step_offset); - LOG(LOGS_WARN, LOGF_Reference, "System clock was stepped by %.6f seconds", -step_offset); + if (LCL_ApplyStepOffset(step_offset)) + LOG(LOGS_WARN, LOGF_Reference, "System clock was stepped by %.6f seconds", -step_offset); } LCL_SetSyncStatus(are_we_synchronised, offset_sd, offset_sd + root_delay / 2.0 + root_dispersion); diff --git a/rtc.c b/rtc.c index 46a6073..2d0f9cd 100644 --- a/rtc.c +++ b/rtc.c @@ -93,9 +93,9 @@ fallback_time_init(void) LCL_ReadCookedTime(&now, NULL); if (now.tv_sec < buf.st_mtime) { - LCL_ApplyStepOffset(now.tv_sec - buf.st_mtime); - LOG(LOGS_INFO, LOGF_Rtc, - "System clock set from driftfile %s", drift_file); + if (LCL_ApplyStepOffset(now.tv_sec - buf.st_mtime)) + LOG(LOGS_INFO, LOGF_Rtc, "System clock set from driftfile %s", + drift_file); } } diff --git a/rtc_linux.c b/rtc_linux.c index ad0a844..40befb3 100644 --- a/rtc_linux.c +++ b/rtc_linux.c @@ -1043,9 +1043,9 @@ RTC_Linux_TimePreInit(void) /* Set system time only if the step is larger than 1 second */ if (fabs(sys_offset) >= 1.0) { - LOG(LOGS_INFO, LOGF_RtcLinux, "Set system time, error in RTC = %f", - accumulated_error); - LCL_ApplyStepOffset(sys_offset); + if (LCL_ApplyStepOffset(sys_offset)) + LOG(LOGS_INFO, LOGF_RtcLinux, "Set system time, error in RTC = %f", + accumulated_error); } } else { LOG(LOGS_WARN, LOGF_RtcLinux, "Could not convert RTC reading to seconds since 1/1/1970"); diff --git a/util.c b/util.c index 7797a3b..e400d96 100644 --- a/util.c +++ b/util.c @@ -606,6 +606,36 @@ UTI_Int64ToTimeval(NTP_int64 *src, /* ================================================== */ +/* Maximum offset between two sane times */ +#define MAX_OFFSET 4294967296.0 + +int +UTI_IsTimeOffsetSane(struct timeval *tv, double offset) +{ + double t; + + /* Handle nan correctly here */ + if (!(offset > -MAX_OFFSET && offset < MAX_OFFSET)) + return 0; + + UTI_TimevalToDouble(tv, &t); + t += offset; + + /* Time before 1970 is not considered valid */ + if (t < 0.0) + return 0; + +#ifdef HAVE_LONG_TIME_T + /* Check if it's in the interval to which NTP time is mapped */ + if (t < (double)NTP_ERA_SPLIT || t > (double)(NTP_ERA_SPLIT + (1LL << 32))) + return 0; +#endif + + return 1; +} + +/* ================================================== */ + void UTI_TimevalNetworkToHost(Timeval *src, struct timeval *dest) { diff --git a/util.h b/util.h index 81fdd7d..5ab9413 100644 --- a/util.h +++ b/util.h @@ -104,6 +104,9 @@ extern void UTI_TimevalToInt64(struct timeval *src, NTP_int64 *dest, uint32_t fu extern void UTI_Int64ToTimeval(NTP_int64 *src, struct timeval *dest); +/* Check if time + offset is sane */ +extern int UTI_IsTimeOffsetSane(struct timeval *tv, double offset); + extern void UTI_TimevalNetworkToHost(Timeval *src, struct timeval *dest); extern void UTI_TimevalHostToNetwork(struct timeval *src, Timeval *dest);