summaryrefslogtreecommitdiffstats
path: root/src/basic
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2024-08-28 14:10:01 +0200
committerLennart Poettering <lennart@poettering.net>2024-09-05 17:40:25 +0200
commit6f5cf41570776f489967d1a7de18260b2bc9acf9 (patch)
tree0c94612f28294ccf45e6f737c439d0f921c88cc2 /src/basic
parenthwclock-util: the struct tm parameter is not a pure return parameter, it's al... (diff)
downloadsystemd-6f5cf41570776f489967d1a7de18260b2bc9acf9.tar.xz
systemd-6f5cf41570776f489967d1a7de18260b2bc9acf9.zip
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
Diffstat (limited to 'src/basic')
-rw-r--r--src/basic/log.c8
-rw-r--r--src/basic/os-util.c21
-rw-r--r--src/basic/time-util.c70
-rw-r--r--src/basic/time-util.h4
4 files changed, 70 insertions, 33 deletions
diff --git a/src/basic/log.c b/src/basic/log.c
index c3e61ab5d4..b99f37385c 100644
--- a/src/basic/log.c
+++ b/src/basic/log.c
@@ -536,8 +536,8 @@ static int write_to_syslog(
char header_priority[2 + DECIMAL_STR_MAX(int) + 1],
header_time[64],
header_pid[4 + DECIMAL_STR_MAX(pid_t) + 1];
- time_t t;
struct tm tm;
+ int r;
if (syslog_fd < 0)
return 0;
@@ -547,9 +547,9 @@ static int write_to_syslog(
xsprintf(header_priority, "<%i>", level);
- t = (time_t) (now(CLOCK_REALTIME) / USEC_PER_SEC);
- if (!localtime_r(&t, &tm))
- return -EINVAL;
+ r = localtime_or_gmtime_usec(now(CLOCK_REALTIME), /* utc= */ false, &tm);
+ if (r < 0)
+ return r;
if (strftime(header_time, sizeof(header_time), "%h %e %T ", &tm) <= 0)
return -EINVAL;
diff --git a/src/basic/os-util.c b/src/basic/os-util.c
index 9b31a0d325..4eec2f6014 100644
--- a/src/basic/os-util.c
+++ b/src/basic/os-util.c
@@ -450,24 +450,29 @@ int os_release_support_ended(const char *support_end, bool quiet, usec_t *ret_eo
support_end = _support_end_alloc;
}
- if (isempty(support_end)) /* An empty string is a explicit way to say "no EOL exists" */
+ if (isempty(support_end)) { /* An empty string is a explicit way to say "no EOL exists" */
+ if (ret_eol)
+ *ret_eol = USEC_INFINITY;
+
return false; /* no end date defined */
+ }
struct tm tm = {};
const char *k = strptime(support_end, "%Y-%m-%d", &tm);
if (!k || *k)
return log_full_errno(quiet ? LOG_DEBUG : LOG_WARNING, SYNTHETIC_ERRNO(EINVAL),
- "Failed to parse SUPPORT_END= in os-release file, ignoring: %m");
+ "Failed to parse SUPPORT_END= from os-release file, ignoring: %s", support_end);
- time_t eol = timegm(&tm);
- if (eol == (time_t) -1)
- return log_full_errno(quiet ? LOG_DEBUG : LOG_WARNING, SYNTHETIC_ERRNO(EINVAL),
- "Failed to convert SUPPORT_END= in os-release file, ignoring: %m");
+ usec_t eol;
+ r = mktime_or_timegm_usec(&tm, /* utc= */ true, &eol);
+ if (r < 0)
+ return log_full_errno(quiet ? LOG_DEBUG : LOG_WARNING, r,
+ "Failed to convert SUPPORT_END= time from os-release file, ignoring: %m");
if (ret_eol)
- *ret_eol = eol * USEC_PER_SEC;
+ *ret_eol = eol;
- return DIV_ROUND_UP(now(CLOCK_REALTIME), USEC_PER_SEC) > (usec_t) eol;
+ return now(CLOCK_REALTIME) > eol;
}
const char* os_release_pretty_name(const char *pretty_name, const char *name) {
diff --git a/src/basic/time-util.c b/src/basic/time-util.c
index ed6fdc3e49..ac2b078225 100644
--- a/src/basic/time-util.c
+++ b/src/basic/time-util.c
@@ -332,7 +332,6 @@ char* format_timestamp_style(
struct tm tm;
bool utc, us;
- time_t sec;
size_t n;
assert(buf);
@@ -375,9 +374,7 @@ char* format_timestamp_style(
return strcpy(buf, xxx[style]);
}
- sec = (time_t) (t / USEC_PER_SEC); /* Round down */
-
- if (!localtime_or_gmtime_r(&sec, &tm, utc))
+ if (localtime_or_gmtime_usec(t, utc, &tm) < 0)
return NULL;
/* Start with the week day */
@@ -665,7 +662,6 @@ static int parse_timestamp_impl(
unsigned fractional = 0;
const char *k;
struct tm tm, copy;
- time_t sec;
/* Allowed syntaxes:
*
@@ -778,10 +774,9 @@ static int parse_timestamp_impl(
}
}
- sec = (time_t) (usec / USEC_PER_SEC);
-
- if (!localtime_or_gmtime_r(&sec, &tm, utc))
- return -EINVAL;
+ r = localtime_or_gmtime_usec(usec, utc, &tm);
+ if (r < 0)
+ return r;
tm.tm_isdst = isdst;
@@ -939,11 +934,11 @@ from_tm:
} else
minus = gmtoff * USEC_PER_SEC;
- sec = mktime_or_timegm(&tm, utc);
- if (sec < 0)
- return -EINVAL;
+ r = mktime_or_timegm_usec(&tm, utc, &usec);
+ if (r < 0)
+ return r;
- usec = usec_add(sec * USEC_PER_SEC, fractional);
+ usec = usec_add(usec, fractional);
finish:
usec = usec_add(usec, plus);
@@ -1625,17 +1620,54 @@ int get_timezone(char **ret) {
return strdup_to(ret, e);
}
-time_t mktime_or_timegm(struct tm *tm, bool utc) {
+int mktime_or_timegm_usec(
+ struct tm *tm, /* input + normalized output */
+ bool utc,
+ usec_t *ret) {
+
+ time_t t;
+
assert(tm);
- return utc ? timegm(tm) : mktime(tm);
+ 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!)*/
+ return -ERANGE;
+ 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 */
+ return -ERANGE;
+
+ /* timegm()/mktime() is a bit weird to use, since it returns -1 in two cases: on error as well as a
+ * valid time indicating one second before the UNIX epoch. Let's treat both cases the same here, and
+ * return -ERANGE for anything negative, since usec_t is unsigned, and we can thus not express
+ * negative times anyway. */
+
+ t = utc ? timegm(tm) : mktime(tm);
+ if (t < 0) /* Refuse negative times and errors */
+ return -ERANGE;
+ if ((usec_t) t >= USEC_INFINITY / USEC_PER_SEC) /* Never return USEC_INFINITY by accident (or overflow) */
+ return -ERANGE;
+
+ if (ret)
+ *ret = (usec_t) t * USEC_PER_SEC;
+ return 0;
}
-struct tm *localtime_or_gmtime_r(const time_t *t, struct tm *tm, bool utc) {
- assert(t);
- assert(tm);
+int localtime_or_gmtime_usec(
+ usec_t t,
+ bool utc,
+ struct tm *ret) {
- return utc ? gmtime_r(t, tm) : localtime_r(t, tm);
+ t /= USEC_PER_SEC; /* Round down */
+ if (t > (usec_t) TIME_T_MAX)
+ return -ERANGE;
+ time_t sec = (time_t) t;
+
+ struct tm buf = {};
+ if (!(utc ? gmtime_r(&sec, &buf) : localtime_r(&sec, &buf)))
+ return -EINVAL;
+
+ if (ret)
+ *ret = buf;
+
+ return 0;
}
static uint32_t sysconf_clock_ticks_cached(void) {
diff --git a/src/basic/time-util.h b/src/basic/time-util.h
index f273770233..7d5a1b7b78 100644
--- a/src/basic/time-util.h
+++ b/src/basic/time-util.h
@@ -177,8 +177,8 @@ usec_t usec_shift_clock(usec_t, clockid_t from, clockid_t to);
int get_timezone(char **ret);
-time_t mktime_or_timegm(struct tm *tm, bool utc);
-struct tm *localtime_or_gmtime_r(const time_t *t, struct tm *tm, bool utc);
+int mktime_or_timegm_usec(struct tm *tm, bool utc, usec_t *ret);
+int localtime_or_gmtime_usec(usec_t t, bool utc, struct tm *ret);
uint32_t usec_to_jiffies(usec_t usec);
usec_t jiffies_to_usec(uint32_t jiffies);