summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2021-03-21 20:59:32 +0100
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2021-03-23 10:13:51 +0100
commit822d39cad4156b650b84a1a09dc2932090b228e8 (patch)
tree694562ad5526c18b2066edd95adfb5eb6c84e9a6
parent3bfa393198800857f73863648aca4b3bda276e5e (diff)
downloadsystemd-822d39cad4156b650b84a1a09dc2932090b228e8.tar.gz
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) (cherry picked from commit f14b80e09e225ccf7cfd8a85578b7e64c3fdebb9)
-rw-r--r--src/shared/calendarspec.c14
1 files changed, 13 insertions, 1 deletions
diff --git a/src/shared/calendarspec.c b/src/shared/calendarspec.c
index db6a103c42..d83e0fbded 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) {