Open dmik opened 3 years ago
Here is a simple test case:
#include <stdio.h>
#include <time.h>
#include <string.h>
void test_time(const char *msg, const time64_t t)
{
struct tm tm;
memset(&tm, 0, sizeof(tm));
if (_gmtime64_r(&t, &tm))
printf ("%s: _gmtime64_r: year %d, mon %d, mday %d, hour %d, min %d, sec %d, isdst %d\n", msg, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_isdst);
else
printf ("%s: _gmtime64_r: failed\n", msg);
memset(&tm, 0, sizeof(tm));
if (_localtime64_r(&t, &tm))
printf ("%s: _localtime64_r: year %d, mon %d, mday %d, hour %d, min %d, sec %d, isdst %d\n", msg, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_isdst);
else
printf ("%s: _localtime64_r: failed\n", msg);
}
void test_time_str(const char *msg, const char *str, const char *fmt)
{
struct tm tm;
memset(&tm, 0, sizeof(tm));
if (strptime(str, fmt, &tm))
printf ("%s: strptime: year %d, mon %d, mday %d, hour %d, min %d, sec %d, isdst %d yday %d\n", msg, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_isdst, tm.tm_yday);
else
printf ("%s: strptime: failed\n", msg);
}
int main()
{
const time64_t y2038 = 2158963200LL; // midnight, 01-Jun-2038 GMT
const time64_t y2060 = 2853273600LL; // midnight, 01-Jun-2060 GMT
test_time("y2038", y2038);
test_time("y2060", y2060);
const char *fmt1 = "%Y-%m-%d %H:%M:%S";
const char *y2038_str1 = "2038-06-01 00:00:00";
const char *y2060_str1 = "2060-06-01 00:00:00";
test_time_str("y2038_str1", y2038_str1, fmt1);
test_time_str("y2060_str1", y2060_str1, fmt1);
const char *fmt2 = "%Y %j";
const char *y2038_str2 = "2038 1";
const char *y2060_str2 = "2070 1";
test_time_str("y2038_str2", y2038_str2, fmt2);
test_time_str("y2060_str2", y2060_str2, fmt2);
return 0;
}
I could trigger the gmtime
failure and the localtime
crash (because it doesn't check for gmtime
returning NULL) for year 2060 and further. However, I couldn't trigger any problems with strptime
so far (more testing needed).
I'm postponing this until we decide what's the best route here.
As shown in https://github.com/bitwiseworks/libc/issues/109#issuecomment-882764025, kLIBC supports years from 1900 till 2059 when handling time in
localtime
,gmtime
,mktime
and friends, see https://github.com/bitwiseworks/libc/blob/2437044df1ec3ed3a1b737e9127ca68a55f8b6ab/src/emx/include/emx/time.h#L13The EMX-specific _YEARS constant defines the size of various internal structures used to track DST start / stop moments. These structures are also used to convert time between Unix and OS/2 DATETIME.
The main internal structure is
_year_day
which is the number of days since 1970-01-01 till January, 1st of a year in question. It uses short integer (i.e. with a range of days from -32768 to 32767). This is why year 2060 is top of this list.Having a real date with year 2060 or later (or feeding functions with years below 1900 or above 2060) will cause the
gmtime
function (also sed bylocaltime
) to just return NULL instead of a validtm
struct. For the case of__settime
(used to convert unix time to DATETIME) I see an infinite loop of the binary search cycle resulting into an application hang or a crash due to arbitrary memory access. Also,strptime
seems to be affected which doesn't check for year's bounds and assumes_year_day
contains all years of the infinity (causing arbitrary memory access for years less than 1900 or greater than 2059.We need to deal with that at some point if we want LIBC to handle dates after year 2059.
Ideas are welcome.