BUG/MEDIUM: clock: make sure now_ms cannot be TICK_ETERNITY

In clock ticks, 0 is TICK_ETERNITY. Long ago we used to make sure now_ms
couldn't be zero so that it could be assigned to expiration timers, but
it has long changed after functions like tick_add() were instrumented to
make the check. The problem is that aside the rare few accidental direct
assignments to expiration dates, it's also used to mark the beginning of
an event that's later checked against TICK_ETERNITY to know if it has
already struck. The problem in this case is that certain events may just
be replaced or dropped just because they apparently never appeared. It's
probably the case for stconn's "lra" and "fsb" fields, just like it is
for all those involving tick_add_ifset(), like h2c->idle_start.

The right approach would be to change the type of now_ms to something
else that cannot take direct computations and that represents a timestamp,
forcing to always use the conversion functions. The variables holding such
timestamps would also be distinguished from intervals. At first glance we
could have for timestamps:
  - 0 = never happened (for the past), eternity (for the future)
  - X = date
and for intervals:
  - 0 = not set
  - X = interval

However this requires significant changes. Instead for now, let's just
make sure again that now_ms is never 0 by setting it to 1 when this
happens (1 / 4 billion times, or 1ms every 49.7 days).

This will need to be carefully backported to older versions. Note that
with this patch backported, the previous ones fixing the zero date are
not strictly needed.
This commit is contained in:
Willy Tarreau 2024-11-15 15:52:17 +01:00
parent ed55ff878d
commit 5a3735a155
1 changed files with 12 additions and 1 deletions

View File

@ -254,6 +254,10 @@ void clock_update_local_date(int max_wait, int interrupted)
now_ns = date_ns + ofs;
}
now_ms = ns_to_ms(now_ns);
/* correct for TICK_ETNERITY (0) */
if (unlikely(now_ms == TICK_ETERNITY))
now_ms++;
}
void clock_update_global_date()
@ -285,6 +289,9 @@ void clock_update_global_date()
* what we're doing here.
*/
now_ms = ns_to_ms(now_ns);
/* correct for TICK_ETNERITY (0) */
if (unlikely(now_ms == TICK_ETERNITY))
now_ms++;
if (!((now_ns ^ old_now_ns) & ~0x7FFFULL))
return;
@ -330,7 +337,11 @@ void clock_init_process_date(void)
now_offset = sec_to_ns((uint)((uint)(-global_now_ms) / 1000U - BOOT_TIME_WRAP_SEC));
global_now_ns += now_offset;
now_ns = global_now_ns;
now_ms = global_now_ms = ns_to_ms(now_ns);
now_ms = ns_to_ms(now_ns);
/* correct for TICK_ETNERITY (0) */
if (now_ms == TICK_ETERNITY)
now_ms++;
global_now_ms = now_ms;
th_ctx->idle_pct = 100;
clock_update_date(0, 1);