summaryrefslogtreecommitdiffstats
path: root/sys-utils/hwclock.c
diff options
context:
space:
mode:
authorStanislav Brabec2014-05-05 20:49:29 +0200
committerKarel Zak2014-05-06 12:49:55 +0200
commitf196fd1a5f8fff63635fd88b5a0f0bbc96978df2 (patch)
treed30789725ca1e48f5471ffba7a159a5ec012a504 /sys-utils/hwclock.c
parentchsh: Improve the typesetting of the manual (diff)
downloadkernel-qcow2-util-linux-f196fd1a5f8fff63635fd88b5a0f0bbc96978df2.tar.gz
kernel-qcow2-util-linux-f196fd1a5f8fff63635fd88b5a0f0bbc96978df2.tar.xz
kernel-qcow2-util-linux-f196fd1a5f8fff63635fd88b5a0f0bbc96978df2.zip
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 <sbrabec@suse.cz>
Diffstat (limited to 'sys-utils/hwclock.c')
-rw-r--r--sys-utils/hwclock.c38
1 files changed, 28 insertions, 10 deletions
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;