From: Lennart Poettering Date: Thu, 2 Apr 2026 12:11:00 +0000 (+0200) Subject: iso9660: add extra paranoia when converting dates X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8953262390fb148803fd2a7aa1e7f994ca6cfd97;p=thirdparty%2Fsystemd.git iso9660: add extra paranoia when converting dates the ISO9660 date range and the "struct tm" range are quite different, let's add extra paranoia checks that we can always convert the dates without issues or fail cleanly. --- diff --git a/src/repart/iso9660.c b/src/repart/iso9660.c index 3f200942b2d..ef10d05bf26 100644 --- a/src/repart/iso9660.c +++ b/src/repart/iso9660.c @@ -21,6 +21,26 @@ void iso9660_datetime_zero(struct iso9660_datetime *ret) { ret->zone = 0; } +static int validate_tm(const struct tm *t) { + assert(t); + + /* Safety checks on bounded fields of struct tm, ranges as per tm(3type). Mostly in place because + * ISO9660 date/time ranges and struct tm ranges differ. */ + + if (t->tm_mon < 0 || t->tm_mon > 11) + return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Month out of range."); + if (t->tm_mday < 1 || t->tm_mday > 31) + return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Day of month out of range."); + if (t->tm_hour < 0 || t->tm_hour > 23) + return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Hour out of range."); + if (t->tm_min < 0 || t->tm_min > 59) + return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Minute out of range."); + if (t->tm_sec < 0 || t->tm_sec > 60) + return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Seconds out of range."); + + return 0; +} + int iso9660_datetime_from_usec(usec_t usec, bool utc, struct iso9660_datetime *ret) { struct tm t; int r; @@ -31,11 +51,19 @@ int iso9660_datetime_from_usec(usec_t usec, bool utc, struct iso9660_datetime *r if (r < 0) return r; + r = validate_tm(&t); + if (r < 0) + return r; + if (t.tm_year >= 10000 - 1900) return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Year has more than 4 digits and is incompatible with ISO9660."); if (t.tm_year + 1900 < 0) return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Year is negative and is incompatible with ISO9660."); + long offset = t.tm_gmtoff / (15*60); /* The time zone is encoded by 15 minutes increments */ + if (offset < INT8_MIN || offset > INT8_MAX) + return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "GMT offset out of range."); + char buf[17]; /* Ignore leap seconds, no real hope for hardware. Deci-seconds always zero. */ xsprintf(buf, "%04d%02d%02d%02d%02d%02d00", @@ -43,8 +71,7 @@ int iso9660_datetime_from_usec(usec_t usec, bool utc, struct iso9660_datetime *r t.tm_hour, t.tm_min, MIN(t.tm_sec, 59)); memcpy(ret, buf, sizeof(buf)-1); - /* The time zone is encoded by 15 minutes increments */ - ret->zone = t.tm_gmtoff / (15*60); + ret->zone = offset; return 0; } @@ -59,8 +86,16 @@ int iso9660_dir_datetime_from_usec(usec_t usec, bool utc, struct iso9660_dir_tim if (r < 0) return r; + r = validate_tm(&t); + if (r < 0) + return r; + if (t.tm_year < 0 || t.tm_year > UINT8_MAX) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Year is incompatible with ISO9660."); + return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Year is incompatible with ISO9660."); + + long offset = t.tm_gmtoff / (15*60); /* The time zone is encoded by 15 minutes increments */ + if (offset < INT8_MIN || offset > INT8_MAX) + return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "GMT offset out of range."); *ret = (struct iso9660_dir_time) { .year = t.tm_year, @@ -70,7 +105,7 @@ int iso9660_dir_datetime_from_usec(usec_t usec, bool utc, struct iso9660_dir_tim .minute = t.tm_min, .second = MIN(t.tm_sec, 59), /* The time zone is encoded by 15 minutes increments */ - .offset = t.tm_gmtoff / (15*60), + .offset = offset, }; return 0;