]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
hwclock: Prevent excessive drift values
authorStanislav Brabec <sbrabec@suse.cz>
Mon, 5 May 2014 18:49:29 +0000 (20:49 +0200)
committerKarel Zak <kzak@redhat.com>
Tue, 6 May 2014 10:49:55 +0000 (12:49 +0200)
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>
sys-utils/hwclock.c

index 395b5c389a015b4385eab0a4564947686bf26227..5e786a84a4ecf4ddb578c7b7a65a94c90308a3e0 100644 (file)
@@ -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;