1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 11:57:35 +00:00

LibC: Return early in time_to_tm for large time_t

POSIX says that localtime should fail with EOVERFLOW if the result
cannot be represented, which is the case for most large (in absolute
value) time_t inputs, since they overflow (or underflow) tm_year, which
is an int. This patch introduces this functionality. Previously, tm_year
just overflowed (or underflowed) silently.

Incidentally, this partially fixes #12729 without solving the root
problem, which is that time_to_tm is linear in its input to determine
the number of years since epoch.

This means that the bash port mktime test no longer times-out in 60s,
but it still fails (faster) in some other place. Due to underlying issue
in the algorithm, the worst case inputs still take a couple of seconds
on my machine.
This commit is contained in:
Leonardo Duarte 2022-04-14 21:31:18 -03:00 committed by Tim Flynn
parent a99c4a7a7d
commit 335fae9a71

View file

@ -91,8 +91,17 @@ char* ctime_r(time_t const* t, char* buf)
static int const __seconds_per_day = 60 * 60 * 24; static int const __seconds_per_day = 60 * 60 * 24;
static void time_to_tm(struct tm* tm, time_t t) static struct tm* time_to_tm(struct tm* tm, time_t t)
{ {
// Note: these correspond to the number of seconds from epoch to the dates "Jan 1 00:00:00 -2147483648" and "Dec 31 23:59:59 2147483647",
// respectively, which are the smallest and biggest representable dates without overflowing tm->tm_year, if it is an int.
constexpr time_t smallest_possible_time = -67768040609740800;
constexpr time_t biggest_possible_time = 67768036191676799;
if (t < smallest_possible_time || t > biggest_possible_time) {
errno = EOVERFLOW;
return nullptr;
}
int year = 1970; int year = 1970;
for (; t >= days_in_year(year) * __seconds_per_day; ++year) for (; t >= days_in_year(year) * __seconds_per_day; ++year)
t -= days_in_year(year) * __seconds_per_day; t -= days_in_year(year) * __seconds_per_day;
@ -116,6 +125,8 @@ static void time_to_tm(struct tm* tm, time_t t)
tm->tm_mday = days + 1; tm->tm_mday = days + 1;
tm->tm_wday = day_of_week(year, month, tm->tm_mday); tm->tm_wday = day_of_week(year, month, tm->tm_mday);
tm->tm_mon = month - 1; tm->tm_mon = month - 1;
return tm;
} }
static time_t tm_to_time(struct tm* tm, long timezone_adjust_seconds) static time_t tm_to_time(struct tm* tm, long timezone_adjust_seconds)
@ -140,7 +151,8 @@ static time_t tm_to_time(struct tm* tm, long timezone_adjust_seconds)
tm->tm_yday = day_of_year(1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday); tm->tm_yday = day_of_year(1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday);
time_t days_since_epoch = years_to_days_since_epoch(1900 + tm->tm_year) + tm->tm_yday; time_t days_since_epoch = years_to_days_since_epoch(1900 + tm->tm_year) + tm->tm_yday;
auto timestamp = ((days_since_epoch * 24 + tm->tm_hour) * 60 + tm->tm_min) * 60 + tm->tm_sec + timezone_adjust_seconds; auto timestamp = ((days_since_epoch * 24 + tm->tm_hour) * 60 + tm->tm_min) * 60 + tm->tm_sec + timezone_adjust_seconds;
time_to_tm(tm, timestamp); if (!time_to_tm(tm, timestamp))
return -1;
return timestamp; return timestamp;
} }
@ -163,8 +175,7 @@ struct tm* localtime_r(time_t const* t, struct tm* tm)
if (!t) if (!t)
return nullptr; return nullptr;
time_to_tm(tm, *t - (daylight ? altzone : timezone)); return time_to_tm(tm, *t - (daylight ? altzone : timezone));
return tm;
} }
time_t timegm(struct tm* tm) time_t timegm(struct tm* tm)
@ -182,8 +193,7 @@ struct tm* gmtime_r(time_t const* t, struct tm* tm)
{ {
if (!t) if (!t)
return nullptr; return nullptr;
time_to_tm(tm, *t); return time_to_tm(tm, *t);
return tm;
} }
char* asctime(const struct tm* tm) char* asctime(const struct tm* tm)