Merge pull request #34155 from poettering/gmtime-safe
handle gmtime_r() errors more robustly
This commit is contained in:
+26
-19
@@ -576,9 +576,13 @@ static int calendarspec_from_time_t(CalendarSpec *c, time_t time) {
|
||||
struct tm tm;
|
||||
int r;
|
||||
|
||||
if (!gmtime_r(&time, &tm))
|
||||
if ((usec_t) time > USEC_INFINITY / USEC_PER_SEC)
|
||||
return -ERANGE;
|
||||
|
||||
r = localtime_or_gmtime_usec((usec_t) time * USEC_PER_SEC, /* utc= */ true, &tm);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (tm.tm_year > INT_MAX - 1900)
|
||||
return -ERANGE;
|
||||
|
||||
@@ -1094,12 +1098,12 @@ int calendar_spec_from_string(const char *p, CalendarSpec **ret) {
|
||||
}
|
||||
|
||||
static int find_end_of_month(const struct tm *tm, bool utc, int day) {
|
||||
struct tm t = *tm;
|
||||
struct tm t = *ASSERT_PTR(tm);
|
||||
|
||||
t.tm_mon++;
|
||||
t.tm_mday = 1 - day;
|
||||
|
||||
if (mktime_or_timegm(&t, utc) < 0 ||
|
||||
if (mktime_or_timegm_usec(&t, utc, /* ret= */ NULL) < 0 ||
|
||||
t.tm_mon != tm->tm_mon)
|
||||
return -1;
|
||||
|
||||
@@ -1171,8 +1175,8 @@ static int find_matching_component(
|
||||
}
|
||||
|
||||
static int tm_within_bounds(struct tm *tm, bool utc) {
|
||||
struct tm t;
|
||||
int cmp;
|
||||
int r;
|
||||
|
||||
assert(tm);
|
||||
|
||||
/*
|
||||
@@ -1183,9 +1187,10 @@ static int tm_within_bounds(struct tm *tm, bool utc) {
|
||||
if (tm->tm_year + 1900 > MAX_YEAR)
|
||||
return -ERANGE;
|
||||
|
||||
t = *tm;
|
||||
if (mktime_or_timegm(&t, utc) < 0)
|
||||
return negative_errno();
|
||||
struct tm t = *tm;
|
||||
r = mktime_or_timegm_usec(&t, utc, /* ret= */ NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/*
|
||||
* Did any normalization take place? If so, it was out of bounds before.
|
||||
@@ -1194,6 +1199,7 @@ static int tm_within_bounds(struct tm *tm, bool utc) {
|
||||
* out of bounds. Normalization has occurred implies find_matching_component() > 0,
|
||||
* other sub time units are already reset in find_next().
|
||||
*/
|
||||
int cmp;
|
||||
if ((cmp = CMP(t.tm_year, tm->tm_year)) != 0)
|
||||
t.tm_mon = 0;
|
||||
else if ((cmp = CMP(t.tm_mon, tm->tm_mon)) != 0)
|
||||
@@ -1222,7 +1228,7 @@ static bool matches_weekday(int weekdays_bits, const struct tm *tm, bool utc) {
|
||||
return true;
|
||||
|
||||
t = *tm;
|
||||
if (mktime_or_timegm(&t, utc) < 0)
|
||||
if (mktime_or_timegm_usec(&t, utc, /* ret= */ NULL) < 0)
|
||||
return false;
|
||||
|
||||
k = t.tm_wday == 0 ? 6 : t.tm_wday - 1;
|
||||
@@ -1248,7 +1254,7 @@ static int find_next(const CalendarSpec *spec, struct tm *tm, usec_t *usec) {
|
||||
|
||||
for (unsigned iteration = 0; iteration < MAX_CALENDAR_ITERATIONS; iteration++) {
|
||||
/* Normalize the current date */
|
||||
(void) mktime_or_timegm(&c, spec->utc);
|
||||
(void) mktime_or_timegm_usec(&c, spec->utc, /* ret= */ NULL);
|
||||
c.tm_isdst = spec->dst;
|
||||
|
||||
c.tm_year += 1900;
|
||||
@@ -1354,10 +1360,9 @@ static int find_next(const CalendarSpec *spec, struct tm *tm, usec_t *usec) {
|
||||
}
|
||||
|
||||
static int calendar_spec_next_usec_impl(const CalendarSpec *spec, usec_t usec, usec_t *ret_next) {
|
||||
struct tm tm;
|
||||
time_t t;
|
||||
int r;
|
||||
usec_t tm_usec;
|
||||
struct tm tm;
|
||||
int r;
|
||||
|
||||
assert(spec);
|
||||
|
||||
@@ -1365,20 +1370,22 @@ static int calendar_spec_next_usec_impl(const CalendarSpec *spec, usec_t usec, u
|
||||
return -EINVAL;
|
||||
|
||||
usec++;
|
||||
t = (time_t) (usec / USEC_PER_SEC);
|
||||
assert_se(localtime_or_gmtime_r(&t, &tm, spec->utc));
|
||||
r = localtime_or_gmtime_usec(usec, spec->utc, &tm);
|
||||
if (r < 0)
|
||||
return r;
|
||||
tm_usec = usec % USEC_PER_SEC;
|
||||
|
||||
r = find_next(spec, &tm, &tm_usec);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
t = mktime_or_timegm(&tm, spec->utc);
|
||||
if (t < 0)
|
||||
return -EINVAL;
|
||||
usec_t t;
|
||||
r = mktime_or_timegm_usec(&tm, spec->utc, &t);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (ret_next)
|
||||
*ret_next = (usec_t) t * USEC_PER_SEC + tm_usec;
|
||||
*ret_next = t + tm_usec;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -53,11 +53,13 @@ int clock_is_localtime(const char *adjtime_path) {
|
||||
}
|
||||
|
||||
int clock_set_timezone(int *ret_minutesdelta) {
|
||||
struct timespec ts;
|
||||
struct tm tm;
|
||||
int r;
|
||||
|
||||
r = localtime_or_gmtime_usec(now(CLOCK_REALTIME), /* utc= */ false, &tm);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
|
||||
assert_se(localtime_r(&ts.tv_sec, &tm));
|
||||
int minutesdelta = tm.tm_gmtoff / 60;
|
||||
|
||||
struct timezone tz = {
|
||||
|
||||
+60
-48
@@ -368,6 +368,7 @@ static int output_timestamp_realtime(
|
||||
usec_t usec) {
|
||||
|
||||
char buf[CONST_MAX(FORMAT_TIMESTAMP_MAX, 64U)];
|
||||
int r;
|
||||
|
||||
assert(f);
|
||||
assert(j);
|
||||
@@ -375,65 +376,76 @@ static int output_timestamp_realtime(
|
||||
if (!VALID_REALTIME(usec))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No valid realtime timestamp available.");
|
||||
|
||||
if (IN_SET(mode, OUTPUT_SHORT_FULL, OUTPUT_WITH_UNIT)) {
|
||||
const char *k;
|
||||
switch (mode) {
|
||||
|
||||
if (flags & OUTPUT_UTC)
|
||||
k = format_timestamp_style(buf, sizeof(buf), usec, TIMESTAMP_UTC);
|
||||
else
|
||||
k = format_timestamp(buf, sizeof(buf), usec);
|
||||
if (!k)
|
||||
case OUTPUT_SHORT_FULL:
|
||||
case OUTPUT_WITH_UNIT: {
|
||||
if (!format_timestamp_style(buf, sizeof(buf), usec, flags & OUTPUT_UTC ? TIMESTAMP_UTC : TIMESTAMP_PRETTY))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Failed to format timestamp: %" PRIu64, usec);
|
||||
break;
|
||||
}
|
||||
|
||||
} else {
|
||||
case OUTPUT_SHORT_UNIX:
|
||||
xsprintf(buf, "%10" PRI_USEC ".%06" PRI_USEC, usec / USEC_PER_SEC, usec % USEC_PER_SEC);
|
||||
break;
|
||||
|
||||
case OUTPUT_SHORT:
|
||||
case OUTPUT_SHORT_PRECISE:
|
||||
case OUTPUT_SHORT_ISO:
|
||||
case OUTPUT_SHORT_ISO_PRECISE: {
|
||||
struct tm tm;
|
||||
time_t t;
|
||||
size_t tail = 0;
|
||||
|
||||
t = (time_t) (usec / USEC_PER_SEC);
|
||||
|
||||
switch (mode) {
|
||||
|
||||
case OUTPUT_SHORT_UNIX:
|
||||
xsprintf(buf, "%10"PRI_TIME".%06"PRIu64, t, usec % USEC_PER_SEC);
|
||||
break;
|
||||
|
||||
case OUTPUT_SHORT_ISO:
|
||||
case OUTPUT_SHORT_ISO_PRECISE: {
|
||||
size_t tail = strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S",
|
||||
localtime_or_gmtime_r(&t, &tm, flags & OUTPUT_UTC));
|
||||
if (tail == 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to format ISO time.");
|
||||
|
||||
/* No usec in strftime, need to append */
|
||||
if (mode == OUTPUT_SHORT_ISO_PRECISE) {
|
||||
assert_se(snprintf_ok(buf + tail, ELEMENTSOF(buf) - tail, ".%06"PRI_USEC, usec % USEC_PER_SEC));
|
||||
tail += 7;
|
||||
}
|
||||
|
||||
int h = tm.tm_gmtoff / 60 / 60;
|
||||
int m = labs((tm.tm_gmtoff / 60) % 60);
|
||||
snprintf(buf + tail, ELEMENTSOF(buf) - tail, "%+03d:%02d", h, m);
|
||||
break;
|
||||
r = localtime_or_gmtime_usec(usec, flags & OUTPUT_UTC, &tm);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to convert timestamp to calendar time, generating fallback timestamp: %m");
|
||||
else {
|
||||
tail = strftime(
|
||||
buf, sizeof(buf),
|
||||
IN_SET(mode, OUTPUT_SHORT_ISO, OUTPUT_SHORT_ISO_PRECISE) ? "%Y-%m-%dT%H:%M:%S" : "%b %d %H:%M:%S",
|
||||
&tm);
|
||||
if (tail <= 0)
|
||||
log_debug("Failed to format calendar time, generating fallback timestamp.");
|
||||
}
|
||||
|
||||
case OUTPUT_SHORT:
|
||||
case OUTPUT_SHORT_PRECISE:
|
||||
if (tail <= 0) {
|
||||
/* Generate fallback timestamp if regular formatting didn't work. (This might happen on systems where time_t is 32bit) */
|
||||
|
||||
if (strftime(buf, sizeof(buf), "%b %d %H:%M:%S",
|
||||
localtime_or_gmtime_r(&t, &tm, flags & OUTPUT_UTC)) <= 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to format syslog time.");
|
||||
static const char *const xxx[_OUTPUT_MODE_MAX] = {
|
||||
[OUTPUT_SHORT] = "XXX XX XX:XX:XX",
|
||||
[OUTPUT_SHORT_PRECISE] = "XXX XX XX:XX:XX.XXXXXX",
|
||||
[OUTPUT_SHORT_ISO] = "XXXX-XX-XXTXX:XX:XX+XX:XX",
|
||||
[OUTPUT_SHORT_ISO_PRECISE] = "XXXX-XX-XXTXX:XX:XX.XXXXXX+XX:XX",
|
||||
};
|
||||
|
||||
if (mode == OUTPUT_SHORT_PRECISE) {
|
||||
assert(sizeof(buf) > strlen(buf));
|
||||
if (!snprintf_ok(buf + strlen(buf), sizeof(buf) - strlen(buf), ".%06"PRIu64, usec % USEC_PER_SEC))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to format precise time.");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
fputs(xxx[mode], f);
|
||||
return strlen(xxx[mode]);
|
||||
}
|
||||
|
||||
assert(tail <= sizeof(buf));
|
||||
|
||||
/* No usec in strftime, need to append */
|
||||
if (IN_SET(mode, OUTPUT_SHORT_ISO_PRECISE, OUTPUT_SHORT_PRECISE)) {
|
||||
assert_se(snprintf_ok(buf + tail, sizeof(buf) - tail, ".%06" PRI_USEC, usec % USEC_PER_SEC));
|
||||
|
||||
tail += 7;
|
||||
|
||||
assert(tail <= sizeof(buf));
|
||||
}
|
||||
|
||||
if (IN_SET(mode, OUTPUT_SHORT_ISO, OUTPUT_SHORT_ISO_PRECISE)) {
|
||||
int h = tm.tm_gmtoff / 60 / 60,
|
||||
m = abs((int) ((tm.tm_gmtoff / 60) % 60));
|
||||
|
||||
assert_se(snprintf_ok(buf + tail, sizeof(buf) - tail, "%+03d:%02d", h, m));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
fputs(buf, f);
|
||||
|
||||
Reference in New Issue
Block a user