Version in base suite: 247.3-7+deb11u2 Version in overlay suite: 247.3-7+deb11u3 Base version: systemd_247.3-7+deb11u3 Target version: systemd_247.3-7+deb11u4 Base file: /srv/ftp-master.debian.org/ftp/pool/main/s/systemd/systemd_247.3-7+deb11u3.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/s/systemd/systemd_247.3-7+deb11u4.dsc changelog | 7 patches/series | 2 patches/shared-calendarspec-abort-calculation-after-1000-iteratio.patch | 55 +++++ patches/shared-calendarspec-when-mktime-moves-us-backwards-jump-f.patch | 105 ++++++++++ 4 files changed, 169 insertions(+) diff: /srv/release.debian.org/tmp/gOM8mSoRPJ/systemd-247.3/test/testdata: recursive directory loop diff -Nru systemd-247.3/debian/changelog systemd-247.3/debian/changelog --- systemd-247.3/debian/changelog 2023-04-30 12:56:31.000000000 +0000 +++ systemd-247.3/debian/changelog 2023-06-18 14:55:54.000000000 +0000 @@ -1,3 +1,10 @@ +systemd (247.3-7+deb11u4) bullseye; urgency=medium + + * backport patches to fix a calendar spec calculation hang on DST change + if TZ=Europe/Dublin (Closes: #1033540) + + -- Luca Boccassi Sun, 18 Jun 2023 15:55:54 +0100 + systemd (247.3-7+deb11u3) bullseye; urgency=medium * udev: fix creating /dev/serial/by-id/ symlinks for USB devices. diff -Nru systemd-247.3/debian/patches/series systemd-247.3/debian/patches/series --- systemd-247.3/debian/patches/series 2023-04-30 12:51:17.000000000 +0000 +++ systemd-247.3/debian/patches/series 2023-06-18 14:55:16.000000000 +0000 @@ -37,6 +37,8 @@ time-util-fix-buffer-over-run.patch machined-varlink-fix-double-free.patch Always-free-deserialized_subscribed-on-reload.patch +shared-calendarspec-abort-calculation-after-1000-iteratio.patch +shared-calendarspec-when-mktime-moves-us-backwards-jump-f.patch debian/Use-Debian-specific-config-files.patch debian/Bring-tmpfiles.d-tmp.conf-in-line-with-Debian-defaul.patch debian/Make-run-lock-tmpfs-an-API-fs.patch diff -Nru systemd-247.3/debian/patches/shared-calendarspec-abort-calculation-after-1000-iteratio.patch systemd-247.3/debian/patches/shared-calendarspec-abort-calculation-after-1000-iteratio.patch --- systemd-247.3/debian/patches/shared-calendarspec-abort-calculation-after-1000-iteratio.patch 1970-01-01 00:00:00.000000000 +0000 +++ systemd-247.3/debian/patches/shared-calendarspec-abort-calculation-after-1000-iteratio.patch 2023-06-18 14:55:16.000000000 +0000 @@ -0,0 +1,55 @@ +From: =?utf-8?q?Zbigniew_J=C4=99drzejewski-Szmek?= +Date: Sun, 21 Mar 2021 20:59:32 +0100 +Subject: shared/calendarspec: abort calculation after 1000 iterations + +We have a bug where we seem to enter an infinite loop when running in the +Europe/Dublin timezone. The timezone is "special" because it has negative SAVE +values. The handling of this should obviously be fixed, but let's use a +belt-and-suspenders approach, and gracefully fail if we fail to find an answer +within a specific number of attempts. The code in this function is rather +complex, and it's hard to rule out another bug in the future. + +(cherry picked from commit 169615c9a8cdc54d748d4dfc8279be9b3c2bec44) +--- + src/shared/calendarspec.c | 14 +++++++++++++- + 1 file changed, 13 insertions(+), 1 deletion(-) + +diff --git a/src/shared/calendarspec.c b/src/shared/calendarspec.c +index 7162592..80acc57 100644 +--- a/src/shared/calendarspec.c ++++ b/src/shared/calendarspec.c +@@ -1211,6 +1211,10 @@ static bool matches_weekday(int weekdays_bits, const struct tm *tm, bool utc) { + return (weekdays_bits & (1 << k)); + } + ++/* A safety valve: if we get stuck in the calculation, return an error. ++ * C.f. https://bugzilla.redhat.com/show_bug.cgi?id=1941335. */ ++#define MAX_CALENDAR_ITERATIONS 1000 ++ + static int find_next(const CalendarSpec *spec, struct tm *tm, usec_t *usec) { + struct tm c; + int tm_usec; +@@ -1224,7 +1228,7 @@ static int find_next(const CalendarSpec *spec, struct tm *tm, usec_t *usec) { + c = *tm; + tm_usec = *usec; + +- for (;;) { ++ for (unsigned iteration = 0; iteration < MAX_CALENDAR_ITERATIONS; iteration++) { + /* Normalize the current date */ + (void) mktime_or_timegm(&c, spec->utc); + c.tm_isdst = spec->dst; +@@ -1321,6 +1325,14 @@ static int find_next(const CalendarSpec *spec, struct tm *tm, usec_t *usec) { + *usec = tm_usec; + return 0; + } ++ ++ /* It seems we entered an infinite loop. Let's gracefully return an error instead of hanging or ++ * aborting. This code is also exercised when timers.target is brought up during early boot, so ++ * aborting here is problematic and hard to diagnose for users. */ ++ _cleanup_free_ char *s = NULL; ++ (void) calendar_spec_to_string(spec, &s); ++ return log_warning_errno(SYNTHETIC_ERRNO(EDEADLK), ++ "Infinite loop in calendar calculation: %s", strna(s)); + } + + static int calendar_spec_next_usec_impl(const CalendarSpec *spec, usec_t usec, usec_t *ret_next) { diff -Nru systemd-247.3/debian/patches/shared-calendarspec-when-mktime-moves-us-backwards-jump-f.patch systemd-247.3/debian/patches/shared-calendarspec-when-mktime-moves-us-backwards-jump-f.patch --- systemd-247.3/debian/patches/shared-calendarspec-when-mktime-moves-us-backwards-jump-f.patch 1970-01-01 00:00:00.000000000 +0000 +++ systemd-247.3/debian/patches/shared-calendarspec-when-mktime-moves-us-backwards-jump-f.patch 2023-06-18 14:55:16.000000000 +0000 @@ -0,0 +1,105 @@ +From: =?utf-8?q?Zbigniew_J=C4=99drzejewski-Szmek?= +Date: Mon, 22 Mar 2021 12:51:47 +0100 +Subject: shared/calendarspec: when mktime() moves us backwards, jump forward +MIME-Version: 1.0 +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 8bit + +When trying to calculate the next firing of 'Sun *-*-* 01:00:00', we'd fall +into an infinite loop, because mktime() moves us "backwards": + +Before this patch: +tm_within_bounds: good=0 2021-03-29 01:00:00 → 2021-03-29 00:00:00 +tm_within_bounds: good=0 2021-03-29 01:00:00 → 2021-03-29 00:00:00 +tm_within_bounds: good=0 2021-03-29 01:00:00 → 2021-03-29 00:00:00 +... + +We rely on mktime() normalizing the time. The man page does not say that it'll +move the time forward, but our algorithm relies on this. So let's catch this +case explicitly. + +With this patch: +$ TZ=Europe/Dublin faketime 2021-03-21 build/systemd-analyze calendar --iterations=5 'Sun *-*-* 01:00:00' +Normalized form: Sun *-*-* 01:00:00 + Next elapse: Sun 2021-03-21 01:00:00 GMT + (in UTC): Sun 2021-03-21 01:00:00 UTC + From now: 59min left + Iter. #2: Sun 2021-04-04 01:00:00 IST + (in UTC): Sun 2021-04-04 00:00:00 UTC + From now: 1 weeks 6 days left <---- note the 2 week jump here + Iter. #3: Sun 2021-04-11 01:00:00 IST + (in UTC): Sun 2021-04-11 00:00:00 UTC + From now: 2 weeks 6 days left + Iter. #4: Sun 2021-04-18 01:00:00 IST + (in UTC): Sun 2021-04-18 00:00:00 UTC + From now: 3 weeks 6 days left + Iter. #5: Sun 2021-04-25 01:00:00 IST + (in UTC): Sun 2021-04-25 00:00:00 UTC + From now: 1 months 4 days left + +Fixes https://bugzilla.redhat.com/show_bug.cgi?id=1941335. + +(cherry picked from commit 129cb6e249bef30dc33e08f98f0b27a6de976f6f) +--- + src/shared/calendarspec.c | 19 +++++++++++-------- + src/test/test-calendarspec.c | 3 +++ + test/test-functions | 1 + + 3 files changed, 15 insertions(+), 8 deletions(-) + +diff --git a/src/shared/calendarspec.c b/src/shared/calendarspec.c +index 80acc57..c8d97c3 100644 +--- a/src/shared/calendarspec.c ++++ b/src/shared/calendarspec.c +@@ -1185,15 +1185,18 @@ static int tm_within_bounds(struct tm *tm, bool utc) { + return negative_errno(); + + /* Did any normalization take place? If so, it was out of bounds before */ +- bool good = t.tm_year == tm->tm_year && +- t.tm_mon == tm->tm_mon && +- t.tm_mday == tm->tm_mday && +- t.tm_hour == tm->tm_hour && +- t.tm_min == tm->tm_min && +- t.tm_sec == tm->tm_sec; +- if (!good) ++ int cmp = CMP(t.tm_year, tm->tm_year) ?: ++ CMP(t.tm_mon, tm->tm_mon) ?: ++ CMP(t.tm_mday, tm->tm_mday) ?: ++ CMP(t.tm_hour, tm->tm_hour) ?: ++ CMP(t.tm_min, tm->tm_min) ?: ++ CMP(t.tm_sec, tm->tm_sec); ++ ++ if (cmp < 0) ++ return -EDEADLK; /* Refuse to go backward */ ++ if (cmp > 0) + *tm = t; +- return good; ++ return cmp == 0; + } + + static bool matches_weekday(int weekdays_bits, const struct tm *tm, bool utc) { +diff --git a/src/test/test-calendarspec.c b/src/test/test-calendarspec.c +index e0b7f22..1b04186 100644 +--- a/src/test/test-calendarspec.c ++++ b/src/test/test-calendarspec.c +@@ -218,6 +218,9 @@ int main(int argc, char* argv[]) { + // Confirm that timezones in the Spec work regardless of current timezone + test_next("2017-09-09 20:42:00 Pacific/Auckland", "", 12345, 1504946520000000); + test_next("2017-09-09 20:42:00 Pacific/Auckland", "EET", 12345, 1504946520000000); ++ /* Check that we don't start looping if mktime() moves us backwards */ ++ test_next("Sun *-*-* 01:00:00 Europe/Dublin", "", 1616412478000000, 1617494400000000); ++ test_next("Sun *-*-* 01:00:00 Europe/Dublin", "IST", 1616412478000000, 1617494400000000); + + assert_se(calendar_spec_from_string("test", &c) < 0); + assert_se(calendar_spec_from_string(" utc", &c) < 0); +diff --git a/test/test-functions b/test/test-functions +index 52b52bf..beaf4fa 100644 +--- a/test/test-functions ++++ b/test/test-functions +@@ -1120,6 +1120,7 @@ install_zoneinfo() { + inst_any /usr/share/zoneinfo/Asia/Vladivostok + inst_any /usr/share/zoneinfo/Australia/Sydney + inst_any /usr/share/zoneinfo/Europe/Berlin ++ inst_any /usr/share/zoneinfo/Europe/Dublin + inst_any /usr/share/zoneinfo/Europe/Kiev + inst_any /usr/share/zoneinfo/Pacific/Auckland + inst_any /usr/share/zoneinfo/Pacific/Honolulu