Skip to content

Commit 6f5cf41

Browse files
committed
time-util: rework localtime_or_gmtime() into localtime_or_gmtime_usec()
We typically want to deal in usec_t, hence let's change the prototype accordingly, and do proper range checks. Also, make sure are not confused by negative times. Do something similar for mktime_or_timegm(). This is a more comprehensive alternative to #34065 Replaces: #34065
1 parent 9640db1 commit 6f5cf41

File tree

15 files changed

+290
-162
lines changed

15 files changed

+290
-162
lines changed

src/basic/log.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -536,8 +536,8 @@ static int write_to_syslog(
536536
char header_priority[2 + DECIMAL_STR_MAX(int) + 1],
537537
header_time[64],
538538
header_pid[4 + DECIMAL_STR_MAX(pid_t) + 1];
539-
time_t t;
540539
struct tm tm;
540+
int r;
541541

542542
if (syslog_fd < 0)
543543
return 0;
@@ -547,9 +547,9 @@ static int write_to_syslog(
547547

548548
xsprintf(header_priority, "<%i>", level);
549549

550-
t = (time_t) (now(CLOCK_REALTIME) / USEC_PER_SEC);
551-
if (!localtime_r(&t, &tm))
552-
return -EINVAL;
550+
r = localtime_or_gmtime_usec(now(CLOCK_REALTIME), /* utc= */ false, &tm);
551+
if (r < 0)
552+
return r;
553553

554554
if (strftime(header_time, sizeof(header_time), "%h %e %T ", &tm) <= 0)
555555
return -EINVAL;

src/basic/os-util.c

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -450,24 +450,29 @@ int os_release_support_ended(const char *support_end, bool quiet, usec_t *ret_eo
450450
support_end = _support_end_alloc;
451451
}
452452

453-
if (isempty(support_end)) /* An empty string is a explicit way to say "no EOL exists" */
453+
if (isempty(support_end)) { /* An empty string is a explicit way to say "no EOL exists" */
454+
if (ret_eol)
455+
*ret_eol = USEC_INFINITY;
456+
454457
return false; /* no end date defined */
458+
}
455459

456460
struct tm tm = {};
457461
const char *k = strptime(support_end, "%Y-%m-%d", &tm);
458462
if (!k || *k)
459463
return log_full_errno(quiet ? LOG_DEBUG : LOG_WARNING, SYNTHETIC_ERRNO(EINVAL),
460-
"Failed to parse SUPPORT_END= in os-release file, ignoring: %m");
464+
"Failed to parse SUPPORT_END= from os-release file, ignoring: %s", support_end);
461465

462-
time_t eol = timegm(&tm);
463-
if (eol == (time_t) -1)
464-
return log_full_errno(quiet ? LOG_DEBUG : LOG_WARNING, SYNTHETIC_ERRNO(EINVAL),
465-
"Failed to convert SUPPORT_END= in os-release file, ignoring: %m");
466+
usec_t eol;
467+
r = mktime_or_timegm_usec(&tm, /* utc= */ true, &eol);
468+
if (r < 0)
469+
return log_full_errno(quiet ? LOG_DEBUG : LOG_WARNING, r,
470+
"Failed to convert SUPPORT_END= time from os-release file, ignoring: %m");
466471

467472
if (ret_eol)
468-
*ret_eol = eol * USEC_PER_SEC;
473+
*ret_eol = eol;
469474

470-
return DIV_ROUND_UP(now(CLOCK_REALTIME), USEC_PER_SEC) > (usec_t) eol;
475+
return now(CLOCK_REALTIME) > eol;
471476
}
472477

473478
const char* os_release_pretty_name(const char *pretty_name, const char *name) {

src/basic/time-util.c

Lines changed: 51 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,6 @@ char* format_timestamp_style(
332332

333333
struct tm tm;
334334
bool utc, us;
335-
time_t sec;
336335
size_t n;
337336

338337
assert(buf);
@@ -375,9 +374,7 @@ char* format_timestamp_style(
375374
return strcpy(buf, xxx[style]);
376375
}
377376

378-
sec = (time_t) (t / USEC_PER_SEC); /* Round down */
379-
380-
if (!localtime_or_gmtime_r(&sec, &tm, utc))
377+
if (localtime_or_gmtime_usec(t, utc, &tm) < 0)
381378
return NULL;
382379

383380
/* Start with the week day */
@@ -665,7 +662,6 @@ static int parse_timestamp_impl(
665662
unsigned fractional = 0;
666663
const char *k;
667664
struct tm tm, copy;
668-
time_t sec;
669665

670666
/* Allowed syntaxes:
671667
*
@@ -778,10 +774,9 @@ static int parse_timestamp_impl(
778774
}
779775
}
780776

781-
sec = (time_t) (usec / USEC_PER_SEC);
782-
783-
if (!localtime_or_gmtime_r(&sec, &tm, utc))
784-
return -EINVAL;
777+
r = localtime_or_gmtime_usec(usec, utc, &tm);
778+
if (r < 0)
779+
return r;
785780

786781
tm.tm_isdst = isdst;
787782

@@ -939,11 +934,11 @@ static int parse_timestamp_impl(
939934
} else
940935
minus = gmtoff * USEC_PER_SEC;
941936

942-
sec = mktime_or_timegm(&tm, utc);
943-
if (sec < 0)
944-
return -EINVAL;
937+
r = mktime_or_timegm_usec(&tm, utc, &usec);
938+
if (r < 0)
939+
return r;
945940

946-
usec = usec_add(sec * USEC_PER_SEC, fractional);
941+
usec = usec_add(usec, fractional);
947942

948943
finish:
949944
usec = usec_add(usec, plus);
@@ -1625,17 +1620,54 @@ int get_timezone(char **ret) {
16251620
return strdup_to(ret, e);
16261621
}
16271622

1628-
time_t mktime_or_timegm(struct tm *tm, bool utc) {
1623+
int mktime_or_timegm_usec(
1624+
struct tm *tm, /* input + normalized output */
1625+
bool utc,
1626+
usec_t *ret) {
1627+
1628+
time_t t;
1629+
16291630
assert(tm);
16301631

1631-
return utc ? timegm(tm) : mktime(tm);
1632+
if (tm->tm_year < 69) /* early check for negative (i.e. before 1970) time_t (Note that in some timezones the epoch is in the year 1969!)*/
1633+
return -ERANGE;
1634+
if ((usec_t) tm->tm_year > CONST_MIN(USEC_INFINITY / USEC_PER_YEAR, (usec_t) TIME_T_MAX / (365U * 24U * 60U * 60U)) - 1900) /* early check for possible overrun of usec_t or time_t */
1635+
return -ERANGE;
1636+
1637+
/* timegm()/mktime() is a bit weird to use, since it returns -1 in two cases: on error as well as a
1638+
* valid time indicating one second before the UNIX epoch. Let's treat both cases the same here, and
1639+
* return -ERANGE for anything negative, since usec_t is unsigned, and we can thus not express
1640+
* negative times anyway. */
1641+
1642+
t = utc ? timegm(tm) : mktime(tm);
1643+
if (t < 0) /* Refuse negative times and errors */
1644+
return -ERANGE;
1645+
if ((usec_t) t >= USEC_INFINITY / USEC_PER_SEC) /* Never return USEC_INFINITY by accident (or overflow) */
1646+
return -ERANGE;
1647+
1648+
if (ret)
1649+
*ret = (usec_t) t * USEC_PER_SEC;
1650+
return 0;
16321651
}
16331652

1634-
struct tm *localtime_or_gmtime_r(const time_t *t, struct tm *tm, bool utc) {
1635-
assert(t);
1636-
assert(tm);
1653+
int localtime_or_gmtime_usec(
1654+
usec_t t,
1655+
bool utc,
1656+
struct tm *ret) {
16371657

1638-
return utc ? gmtime_r(t, tm) : localtime_r(t, tm);
1658+
t /= USEC_PER_SEC; /* Round down */
1659+
if (t > (usec_t) TIME_T_MAX)
1660+
return -ERANGE;
1661+
time_t sec = (time_t) t;
1662+
1663+
struct tm buf = {};
1664+
if (!(utc ? gmtime_r(&sec, &buf) : localtime_r(&sec, &buf)))
1665+
return -EINVAL;
1666+
1667+
if (ret)
1668+
*ret = buf;
1669+
1670+
return 0;
16391671
}
16401672

16411673
static uint32_t sysconf_clock_ticks_cached(void) {

src/basic/time-util.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,8 +177,8 @@ usec_t usec_shift_clock(usec_t, clockid_t from, clockid_t to);
177177

178178
int get_timezone(char **ret);
179179

180-
time_t mktime_or_timegm(struct tm *tm, bool utc);
181-
struct tm *localtime_or_gmtime_r(const time_t *t, struct tm *tm, bool utc);
180+
int mktime_or_timegm_usec(struct tm *tm, bool utc, usec_t *ret);
181+
int localtime_or_gmtime_usec(usec_t t, bool utc, struct tm *ret);
182182

183183
uint32_t usec_to_jiffies(usec_t usec);
184184
usec_t jiffies_to_usec(uint32_t jiffies);

src/hostname/hostnamed.c

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -342,14 +342,15 @@ static int get_firmware_date(usec_t *ret) {
342342
.tm_mon = m,
343343
.tm_year = y,
344344
};
345-
time_t v = timegm(&tm);
346-
if (v == (time_t) -1)
347-
return -errno;
345+
346+
usec_t v;
347+
r = mktime_or_timegm_usec(&tm, /* utc= */ true, &v);
348+
if (r < 0)
349+
return r;
348350
if (tm.tm_mday != (int) d || tm.tm_mon != (int) m || tm.tm_year != (int) y)
349351
return -EINVAL; /* date was not normalized? (e.g. "30th of feb") */
350352

351-
*ret = (usec_t) v * USEC_PER_SEC;
352-
353+
*ret = v;
353354
return 0;
354355
}
355356

src/import/curl-util.c

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,6 @@ int curl_parse_http_time(const char *t, usec_t *ret) {
384384
_cleanup_(freelocalep) locale_t loc = (locale_t) 0;
385385
const char *e;
386386
struct tm tm;
387-
time_t v;
388387

389388
assert(t);
390389
assert(ret);
@@ -404,10 +403,5 @@ int curl_parse_http_time(const char *t, usec_t *ret) {
404403
if (!e || *e != 0)
405404
return -EINVAL;
406405

407-
v = timegm(&tm);
408-
if (v == (time_t) -1)
409-
return -EINVAL;
410-
411-
*ret = (usec_t) v * USEC_PER_SEC;
412-
return 0;
406+
return mktime_or_timegm_usec(&tm, /* usec= */ true, ret);
413407
}

src/journal/journald-syslog.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,6 @@ void server_forward_syslog(Server *s, int priority, const char *identifier, cons
127127
char header_priority[DECIMAL_STR_MAX(priority) + 3], header_time[64],
128128
header_pid[STRLEN("[]: ") + DECIMAL_STR_MAX(pid_t) + 1];
129129
int n = 0;
130-
time_t t;
131130
struct tm tm;
132131
_cleanup_free_ char *ident_buf = NULL;
133132

@@ -144,8 +143,7 @@ void server_forward_syslog(Server *s, int priority, const char *identifier, cons
144143
iovec[n++] = IOVEC_MAKE_STRING(header_priority);
145144

146145
/* Second: timestamp */
147-
t = tv ? tv->tv_sec : ((time_t) (now(CLOCK_REALTIME) / USEC_PER_SEC));
148-
if (!localtime_r(&t, &tm))
146+
if (localtime_or_gmtime_usec(tv ? tv->tv_sec * USEC_PER_SEC : now(CLOCK_REALTIME), /* utc= */ false, &tm) < 0)
149147
return;
150148
if (strftime(header_time, sizeof(header_time), "%h %e %T ", &tm) <= 0)
151149
return;

src/resolve/resolved-dns-rr.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -793,12 +793,14 @@ static char* format_location(uint32_t latitude, uint32_t longitude, uint32_t alt
793793

794794
static int format_timestamp_dns(char *buf, size_t l, time_t sec) {
795795
struct tm tm;
796+
int r;
796797

797798
assert(buf);
798799
assert(l > STRLEN("YYYYMMDDHHmmSS"));
799800

800-
if (!gmtime_r(&sec, &tm))
801-
return -EINVAL;
801+
r = localtime_or_gmtime_usec(sec * USEC_PER_SEC, /* utc= */ true, &tm);
802+
if (r < 0)
803+
return r;
802804

803805
if (strftime(buf, l, "%Y%m%d%H%M%S", &tm) <= 0)
804806
return -EINVAL;

src/shared/calendarspec.c

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -576,9 +576,13 @@ static int calendarspec_from_time_t(CalendarSpec *c, time_t time) {
576576
struct tm tm;
577577
int r;
578578

579-
if (!gmtime_r(&time, &tm))
579+
if ((usec_t) time > USEC_INFINITY / USEC_PER_SEC)
580580
return -ERANGE;
581581

582+
r = localtime_or_gmtime_usec((usec_t) time * USEC_PER_SEC, /* utc= */ true, &tm);
583+
if (r < 0)
584+
return r;
585+
582586
if (tm.tm_year > INT_MAX - 1900)
583587
return -ERANGE;
584588

@@ -1094,12 +1098,12 @@ int calendar_spec_from_string(const char *p, CalendarSpec **ret) {
10941098
}
10951099

10961100
static int find_end_of_month(const struct tm *tm, bool utc, int day) {
1097-
struct tm t = *tm;
1101+
struct tm t = *ASSERT_PTR(tm);
10981102

10991103
t.tm_mon++;
11001104
t.tm_mday = 1 - day;
11011105

1102-
if (mktime_or_timegm(&t, utc) < 0 ||
1106+
if (mktime_or_timegm_usec(&t, utc, /* ret= */ NULL) < 0 ||
11031107
t.tm_mon != tm->tm_mon)
11041108
return -1;
11051109

@@ -1171,8 +1175,8 @@ static int find_matching_component(
11711175
}
11721176

11731177
static int tm_within_bounds(struct tm *tm, bool utc) {
1174-
struct tm t;
1175-
int cmp;
1178+
int r;
1179+
11761180
assert(tm);
11771181

11781182
/*
@@ -1183,9 +1187,10 @@ static int tm_within_bounds(struct tm *tm, bool utc) {
11831187
if (tm->tm_year + 1900 > MAX_YEAR)
11841188
return -ERANGE;
11851189

1186-
t = *tm;
1187-
if (mktime_or_timegm(&t, utc) < 0)
1188-
return negative_errno();
1190+
struct tm t = *tm;
1191+
r = mktime_or_timegm_usec(&t, utc, /* ret= */ NULL);
1192+
if (r < 0)
1193+
return r;
11891194

11901195
/*
11911196
* 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) {
11941199
* out of bounds. Normalization has occurred implies find_matching_component() > 0,
11951200
* other sub time units are already reset in find_next().
11961201
*/
1202+
int cmp;
11971203
if ((cmp = CMP(t.tm_year, tm->tm_year)) != 0)
11981204
t.tm_mon = 0;
11991205
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) {
12221228
return true;
12231229

12241230
t = *tm;
1225-
if (mktime_or_timegm(&t, utc) < 0)
1231+
if (mktime_or_timegm_usec(&t, utc, /* ret= */ NULL) < 0)
12261232
return false;
12271233

12281234
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) {
12481254

12491255
for (unsigned iteration = 0; iteration < MAX_CALENDAR_ITERATIONS; iteration++) {
12501256
/* Normalize the current date */
1251-
(void) mktime_or_timegm(&c, spec->utc);
1257+
(void) mktime_or_timegm_usec(&c, spec->utc, /* ret= */ NULL);
12521258
c.tm_isdst = spec->dst;
12531259

12541260
c.tm_year += 1900;
@@ -1354,31 +1360,32 @@ static int find_next(const CalendarSpec *spec, struct tm *tm, usec_t *usec) {
13541360
}
13551361

13561362
static int calendar_spec_next_usec_impl(const CalendarSpec *spec, usec_t usec, usec_t *ret_next) {
1363+
usec_t tm_usec;
13571364
struct tm tm;
1358-
time_t t;
13591365
int r;
1360-
usec_t tm_usec;
13611366

13621367
assert(spec);
13631368

13641369
if (usec > USEC_TIMESTAMP_FORMATTABLE_MAX)
13651370
return -EINVAL;
13661371

13671372
usec++;
1368-
t = (time_t) (usec / USEC_PER_SEC);
1369-
assert_se(localtime_or_gmtime_r(&t, &tm, spec->utc));
1373+
r = localtime_or_gmtime_usec(usec, spec->utc, &tm);
1374+
if (r < 0)
1375+
return r;
13701376
tm_usec = usec % USEC_PER_SEC;
13711377

13721378
r = find_next(spec, &tm, &tm_usec);
13731379
if (r < 0)
13741380
return r;
13751381

1376-
t = mktime_or_timegm(&tm, spec->utc);
1377-
if (t < 0)
1378-
return -EINVAL;
1382+
usec_t t;
1383+
r = mktime_or_timegm_usec(&tm, spec->utc, &t);
1384+
if (r < 0)
1385+
return r;
13791386

13801387
if (ret_next)
1381-
*ret_next = (usec_t) t * USEC_PER_SEC + tm_usec;
1388+
*ret_next = t + tm_usec;
13821389

13831390
return 0;
13841391
}

0 commit comments

Comments
 (0)