]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
BUG/MINOR: clock: validate that now_offset still applies to the current date
authorWilly Tarreau <w@1wt.eu>
Mon, 9 Sep 2024 11:56:18 +0000 (13:56 +0200)
committerWilly Tarreau <w@1wt.eu>
Thu, 12 Sep 2024 17:09:19 +0000 (19:09 +0200)
We want to make sure that now_offset is still valid for the current
date: another thread could very well have updated it by detecting a
backwards jump, and at the very same moment the time got fixed again,
that we retrieve and add to the new offset, which results in a larger
jump. Normally, for this to happen, it would mean that before_poll
was also affected by the jump and was detected before and bounded
within 2 seconds, resulting in max 2 seconds perturbations.

Here we try to detect this situation and fall back to re-adjusting the
offset instead.

It's more of a strengthening of what's done by commit e8b1ad4c2b
("BUG/MEDIUM: clock: also update the date offset on time jumps") than a
pure fix, in that the issue was not direclty observed but it's visibly
possible by reading the code, so this should be backported along with
the patch above. This is related to issue GH #2704.

Note that this could be simplified in terms of operations by migrating
the deadlines to nanoseconds, but this was the path to least intrusive
changes.

src/clock.c

index 041c9bf7159fa5e126a166542abae29c6555d62a..9a33ee64283568246e6f8962e959e09e16c2994c 100644 (file)
@@ -200,8 +200,11 @@ int clock_setup_signal_timer(void *tmr, int sig, int val)
 void clock_update_local_date(int max_wait, int interrupted)
 {
        struct timeval min_deadline, max_deadline;
+       llong ofs = HA_ATOMIC_LOAD(&now_offset);
+       llong date_ns;
 
        gettimeofday(&date, NULL);
+       date_ns = tv_to_ns(&date);
 
        /* compute the minimum and maximum local date we may have reached based
         * on our past date and the associated timeout. There are three possible
@@ -219,6 +222,7 @@ void clock_update_local_date(int max_wait, int interrupted)
 
        if (unlikely(__tv_islt(&date, &before_poll)                    || // big jump backwards
                     (!interrupted && __tv_islt(&date, &min_deadline)) || // small jump backwards
+                    date_ns + ofs >= now_ns + ms_to_ns(max_wait + 100)|| // offset changed by another thread
                     __tv_islt(&max_deadline, &date))) {                  // big jump forwards
                if (!interrupted)
                        now_ns += ms_to_ns(max_wait);
@@ -230,13 +234,13 @@ void clock_update_local_date(int max_wait, int interrupted)
                 * we just left now_ns where it was, the date will not be updated
                 * by clock_update_global_date().
                 */
-               HA_ATOMIC_STORE(&now_offset, now_ns - tv_to_ns(&date));
+               HA_ATOMIC_STORE(&now_offset, now_ns - date_ns);
        } else {
                /* The date is still within expectations. Let's apply the
                 * now_offset to the system date. Note: ofs if made of two
                 * independent signed ints.
                 */
-               now_ns = tv_to_ns(&date) + HA_ATOMIC_LOAD(&now_offset);
+               now_ns = date_ns + ofs;
        }
        now_ms = ns_to_ms(now_ns);
 }