From f196fd1a5f8fff63635fd88b5a0f0bbc96978df2 Mon Sep 17 00:00:00 2001 From: Stanislav Brabec Date: Mon, 5 May 2014 20:49:29 +0200 Subject: 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 --- sys-utils/hwclock.c | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) (limited to 'sys-utils/hwclock.c') diff --git a/sys-utils/hwclock.c b/sys-utils/hwclock.c index 395b5c389..5e786a84a 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; -- cgit v1.2.3-55-g7522