Skip to content

timesyncd: add maximum clock step policy - #43117

Open
dabirt wants to merge 1 commit into
systemd:mainfrom
dabirt:dabirt/timesyncd-max-clock-step
Open

timesyncd: add maximum clock step policy#43117
dabirt wants to merge 1 commit into
systemd:mainfrom
dabirt:dabirt/timesyncd-max-clock-step

Conversation

@dabirt

@dabirt dabirt commented Jul 22, 2026

Copy link
Copy Markdown

Summary

  • add an opt-in MaxClockStepSec= limit for the absolute offset accepted from one NTP response;
  • ignore responses beyond the configured limit and continue with another server;
  • expose the configured limit over D-Bus and document the RTC-less first-boot tradeoff; and
  • preserve the existing behavior by defaulting to infinity.

This implements the policy control requested in #17782. A finite default is intentionally not proposed: systems without a reliable RTC may legitimately require a large initial correction. Administrators that have a trustworthy starting clock can now select an appropriate ceiling.

Testing

Tested from current main at 765dc9691be793e03b6b13b16bb834c0aa216c9e on a Debian testing ARM64 host:

ninja -C build test-timesync systemd-timesyncd
meson test -C build --print-errorlogs test-timesync

1/1 timesync - systemd:test-timesync OK

The unit coverage checks the unlimited default, the exact configured boundary, and positive and negative offsets beyond it. git diff --check and XML well-formedness checks also pass.

Closes #17782.

@github-actions github-actions Bot added documentation tests timesync please-review PR is ready for (re-)review by a maintainer labels Jul 22, 2026
Comment thread src/timesync/timesyncd-manager.h Outdated
Comment thread man/timesyncd.conf.xml Outdated
Comment thread src/timesync/timesyncd-manager.c Outdated
@yuwata yuwata added reviewed/needs-rework 🔨 PR has been reviewed and needs another round of reworks and removed please-review PR is ready for (re-)review by a maintainer labels Jul 23, 2026
Add MaxClockChangeSec= as an opt-in upper bound for the absolute offset
accepted from a single NTP response. Responses beyond the configured limit
are ignored and timesyncd moves to another server.

The default remains infinity for compatibility with RTC-less systems that
need a large initial correction.

Closes systemd#17782.
@dabirt
dabirt force-pushed the dabirt/timesyncd-max-clock-step branch from 2bac6da to 29a4730 Compare July 23, 2026 21:19
@github-actions github-actions Bot added please-review PR is ready for (re-)review by a maintainer and removed reviewed/needs-rework 🔨 PR has been reviewed and needs another round of reworks labels Jul 23, 2026
@dabirt

dabirt commented Jul 23, 2026

Copy link
Copy Markdown
Author

Addressed the review in 29a4730:

  • removed DEFAULT_MAX_CLOCK_STEP_USEC and initialize the field with USEC_INFINITY directly;
  • renamed the setting and corresponding D-Bus/internal identifiers to MaxClockChangeSec= / MaxClockChangeUSec; and
  • changed the warning to use FORMAT_TIMESPAN(..., USEC_PER_MSEC) with %s.

I also converted the newly added test assertions to the current ASSERT_* helpers while updating their names. git diff --check passes; the updated project CI is running.

@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown

Claude review of PR #43117 (29a4730)

Suggestions

  • USEC_INFINITY default is not truly unboundedsrc/timesync/timesyncd-manager.c:367(double) USEC_INFINITY / USEC_PER_SEC yields a finite ~1.84e13 s threshold; short-circuit on USEC_INFINITY to mean "no limit" literally.
  • D-Bus docs not regeneratedsrc/timesync/timesyncd-bus.c:218 — new MaxClockChangeUSec property is missing from the generated man/org.freedesktop.timesync1.xml.

Nits

  • Default-infinity test doesn't exercise the real boundsrc/timesync/test-timesync.c:401e9 is below the accidental finite threshold, so it doesn't verify infinity disables the check.

Workflow run

bool manager_clock_change_is_too_large(Manager *m, double offset) {
assert(m);

return fabs(offset) > (double) m->max_clock_change_usec / USEC_PER_SEC;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude: suggestion: When max_clock_change_usec == USEC_INFINITY (the default), this does not compare against infinity. (double) USEC_INFINITY is the finite double 1.8446744073709552e19, so the threshold becomes ~1.844e13 seconds rather than truly unbounded, and any offset above that would be rejected even under the documented "infinity" default. In practice NTP offsets are bounded well below that, so existing behavior is preserved, but relying on that implicit bound is fragile and the code no longer literally means "no limit". Consider short-circuiting explicitly, e.g. if (m->max_clock_change_usec == USEC_INFINITY) return false; before the floating-point comparison, which also sidesteps the USEC_INFINITY / USEC_PER_SEC precision detail.

SD_BUS_PROPERTY("ServerName", "s", property_get_current_server_name, offsetof(Manager, current_server_name), 0),
SD_BUS_PROPERTY("ServerAddress", "(iay)", property_get_current_server_address, offsetof(Manager, current_server_address), 0),
SD_BUS_PROPERTY("RootDistanceMaxUSec", "t", bus_property_get_usec, offsetof(Manager, root_distance_max_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("MaxClockChangeUSec", "t", bus_property_get_usec, offsetof(Manager, max_clock_change_usec), SD_BUS_VTABLE_PROPERTY_CONST),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude: suggestion: A new read-only D-Bus property MaxClockChangeUSec is added to the manager vtable, but the generated interface documentation in man/org.freedesktop.timesync1.xml is not updated to list it. That file is auto-generated (via tools/update-dbus-docs.py) and kept committed in sync with the exported interface; sibling t-typed const properties (RootDistanceMaxUSec, PollIntervalMinUSec, etc.) all appear there. Please regenerate/update it so the introspection docs match the exported interface.

ASSERT_TRUE(manager_clock_change_is_too_large(m, 5.001));
ASSERT_TRUE(manager_clock_change_is_too_large(m, -5.001));
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude: nit: The default-infinity assertion only exercises 1e9, which is far below the accidental finite threshold (~1.844e13 s) produced by (double) USEC_INFINITY / USEC_PER_SEC. It therefore does not actually verify that the default disables the check for arbitrarily large offsets. If the intent is that infinity means "never too large", consider asserting with an offset above that bound (e.g. 1e14); today such a value would incorrectly report too-large.

@yuwata yuwata left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CIs are unhappy.
Please also consider the report by Claude.

@yuwata yuwata added reviewed/needs-rework 🔨 PR has been reviewed and needs another round of reworks ci-fails/needs-rework 🔥 Please rework this, the CI noticed an issue with the PR and removed please-review PR is ready for (re-)review by a maintainer labels Jul 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci-fails/needs-rework 🔥 Please rework this, the CI noticed an issue with the PR claude-review documentation reviewed/needs-rework 🔨 PR has been reviewed and needs another round of reworks tests timesync

Development

Successfully merging this pull request may close these issues.

timesyncd: maybe add support for time difference threshold, where we log or refuse setting the time, to protect against bad NTP servers

3 participants