From 654e902731ea15a3494a3831b78d2b9f1cd1408d Mon Sep 17 00:00:00 2001 From: Ruediger Meier Date: Fri, 16 May 2014 17:01:43 +0200 Subject: [PATCH] Prevent excessive clock drift calculations Squashed commit of the following: commit f55b4b45126b657fe02f5f0d3d7fde740e6a6247 Author: Karel Zak Date: Tue May 6 12:51:42 2014 +0200 hwclock: fix typo Reported-by: Stanislav Brabec Signed-off-by: Karel Zak commit db8fc5f37728810bdd5b865ac420c31714e35def Author: Stanislav Brabec Date: Mon May 5 20:49:49 2014 +0200 hwclock: Check drift value in /etc/adjtime Due to bug in older versions of hwclock, /etc/adjtime can contain excessive drift value (up to many years per day). Prevent it from applying. Signed-off-by: Stanislav Brabec commit f196fd1a5f8fff63635fd88b5a0f0bbc96978df2 Author: Stanislav Brabec Date: Mon May 5 20:49:29 2014 +0200 hwclock: Prevent excessive drift values Failure of CMOS battery can cause writing of excessive drift values (up to many years per day). This causes excessive hwclock adjustment next time, which may lead to overflow in calculate_adjustment() (and hang before 4a44a54b). Prevent this situation, check drift for limits and reset drift to zero instead. Steps to reproduce: mv /etc/adjtime /etc/adjtime.backup rm /etc/adjtime hwclock --set --date 2001-01-01\ 01:00:00 changing of /etc/adjtime. mv /etc/adjtime /etc/adjtime.saved hwclock --set --date 2001-01-02\ 01:00:01 mv /etc/adjtime.saved /etc/adjtime echo "======= The /etc/adjtime has a \"correct\" look:" cat /etc/adjtime hwclock --debug --systohc --utc echo "======= The /etc/adjtime now has deeply failed drift value:" cat /etc/adjtime mv /etc/adjtime /etc/adjtime.saved hwclock --set --date 2015-01-01\ 01:00:00 mv /etc/adjtime.saved /etc/adjtime hwclock --debug --adjust echo "======= And the last /etc/adjtime:" cat /etc/adjtime mv /etc/adjtime.backup /etc/adjtime hwclock --systohc --utc Signed-off-by: Stanislav Brabec Signed-off-by: Ruediger Meier --- sys-utils/hwclock.c | 47 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/sys-utils/hwclock.c b/sys-utils/hwclock.c index 395b5c3..0abf01f 100644 --- a/sys-utils/hwclock.c +++ b/sys-utils/hwclock.c @@ -91,6 +91,11 @@ struct clock_ops *ur; #define FLOOR(arg) ((arg >= 0 ? (int) arg : ((int) arg) - 1)); +/* Maximal clock adjustment in seconds per day. + (adjtime() glibc call has 2145 seconds limit on i386, so it is good enough for us as well, + 43219 is a maximal safe value preventing exact_adjustment overflow.) */ +#define MAX_DRIFT 2145.0 + const char *adj_file_name = NULL; struct adjtime { @@ -1008,6 +1013,7 @@ adjust_drift_factor(struct adjtime *adjtime_p, double adj_days, cal_days; double exp_drift, unc_drift; double factor_adjust; + double drift_factor; /* Adjusted time units per hardware time unit */ atime_per_htime = 1.0 + adjtime_p->drift_factor / sec_per_day; @@ -1033,16 +1039,28 @@ adjust_drift_factor(struct adjtime *adjtime_p, /* Amount to add to previous drift factor */ factor_adjust = unc_drift / cal_days; - if (debug) - printf(_("Clock drifted %.1f seconds in the past " - "%d seconds in spite of a drift factor of " - "%f seconds/day.\n" - "Adjusting drift factor by %f seconds/day\n"), - unc_drift, - (int)(nowtime - adjtime_p->last_calib_time), - adjtime_p->drift_factor, factor_adjust); - - adjtime_p->drift_factor += factor_adjust; + /* New drift factor */ + drift_factor = adjtime_p->drift_factor + factor_adjust; + + if (abs(drift_factor) > MAX_DRIFT) { + if (debug) + printf(_("Clock drift factor was calculated as " + "%f seconds/day.\n" + "It is far too much. Resetting to zero.\n"), + drift_factor); + drift_factor = 0; + } else { + if (debug) + printf(_("Clock drifted %.1f seconds in the past " + "%d seconds in spite of a drift factor of " + "%f seconds/day.\n" + "Adjusting drift factor by %f seconds/day\n"), + unc_drift, + (int)(nowtime - adjtime_p->last_calib_time), + adjtime_p->drift_factor, factor_adjust); + } + + adjtime_p->drift_factor = drift_factor; } adjtime_p->last_calib_time = nowtime; @@ -1190,9 +1208,12 @@ do_adjustment(struct adjtime *adjtime_p, adjtime_p->dirty = TRUE; } else if (adjtime_p->last_adj_time == 0) { if (debug) - printf(_ - ("Not setting clock because last adjustment time is zero, " - "so history is bad.")); + printf(_("Not setting clock because last adjustment time is zero, " + "so history is bad.\n")); + } else if (abs(adjtime_p->drift_factor) > MAX_DRIFT) { + if (debug) + printf(_("Not setting clock because drift factor %f is far too high.\n"), + adjtime_p->drift_factor); } else { int adjustment; /* Number of seconds we must insert in the Hardware Clock */ -- 1.8.4.5