From 16dafed08b2e4b483176c5ee286134544a4badf8 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 8 Dec 2023 19:10:07 -0600 Subject: [PATCH] msvcrt: Adjust _gmtime64_s() accepted time limits. --- dlls/msvcr120/tests/msvcr120.c | 78 ++++++++++++++++++++++++++++++++++ dlls/msvcrt/tests/time.c | 77 +++++++++++++++++++++++++++++++++ dlls/msvcrt/time.c | 15 ++++++- dlls/ucrtbase/tests/misc.c | 75 ++++++++++++++++++++++++++++++++ include/msvcrt/time.h | 3 ++ 5 files changed, 247 insertions(+), 1 deletion(-) diff --git a/dlls/msvcr120/tests/msvcr120.c b/dlls/msvcr120/tests/msvcr120.c index c76db6582cf..656004f4826 100644 --- a/dlls/msvcr120/tests/msvcr120.c +++ b/dlls/msvcr120/tests/msvcr120.c @@ -35,6 +35,8 @@ #include +#define _MAX__TIME64_T (((__time64_t)0x00000007 << 32) | 0x93406FFF) + #ifdef __i386__ #include "pshpack1.h" struct thiscall_thunk @@ -226,6 +228,10 @@ static wctrans_t (__cdecl *p_wctrans)(const char*); static wint_t (__cdecl *p_towctrans)(wint_t, wctrans_t); static int (__cdecl *p_strcmp)(const char *, const char *); static int (__cdecl *p_strncmp)(const char *, const char *, size_t); +static struct tm* (__cdecl *p_gmtime32)(__time32_t*); +static errno_t (__cdecl *p_gmtime32_s)(struct tm*, __time32_t*); +static struct tm* (__cdecl *p_gmtime64)(__time64_t*); +static errno_t (__cdecl *p_gmtime64_s)(struct tm*, __time64_t*); /* make sure we use the correct errno */ #undef errno @@ -296,6 +302,10 @@ static BOOL init(void) p_errno = (void*)GetProcAddress(module, "_errno"); p_wcreate_locale = (void*)GetProcAddress(module, "_wcreate_locale"); p_free_locale = (void*)GetProcAddress(module, "_free_locale"); + p_gmtime64 = (void*)GetProcAddress(module, "_gmtime64"); + p_gmtime64_s = (void*)GetProcAddress(module, "_gmtime64_s"); + p_gmtime32 = (void*)GetProcAddress(module, "_gmtime32"); + p_gmtime32_s = (void*)GetProcAddress(module, "_gmtime32_s"); SET(p_wctype, "wctype"); SET(p_fegetenv, "fegetenv"); SET(p_fesetenv, "fesetenv"); @@ -1714,6 +1724,73 @@ static void test_strcmp(void) ok( ret == 0, "wrong ret %d\n", ret ); } +static void test_gmtime64(void) +{ + struct tm *ptm, tm; + __time64_t t; + int ret; + + t = -1; + memset(&tm, 0xcc, sizeof(tm)); + ptm = p_gmtime64(&t); + ok(!!ptm, "got NULL.\n"); + ret = p_gmtime64_s(&tm, &t); + ok(!ret, "got %d.\n", ret); + ok(tm.tm_year == 69 && tm.tm_hour == 23 && tm.tm_min == 59 && tm.tm_sec == 59, "got %d, %d, %d, %d.\n", + tm.tm_year, tm.tm_hour, tm.tm_min, tm.tm_sec); + + t = -43200; + memset(&tm, 0xcc, sizeof(tm)); + ptm = p_gmtime64(&t); + ok(!!ptm, "got NULL.\n"); + ret = p_gmtime64_s(&tm, &t); + ok(!ret, "got %d.\n", ret); + ok(tm.tm_year == 69 && tm.tm_hour == 12 && tm.tm_min == 0 && tm.tm_sec == 0, "got %d, %d, %d, %d.\n", + tm.tm_year, tm.tm_hour, tm.tm_min, tm.tm_sec); + ptm = p_gmtime32((__time32_t *)&t); + ok(!!ptm, "got NULL.\n"); + memset(&tm, 0xcc, sizeof(tm)); + ret = p_gmtime32_s(&tm, (__time32_t *)&t); + ok(!ret, "got %d.\n", ret); + todo_wine_if(tm.tm_year == 69 && tm.tm_hour == 12) + ok(tm.tm_year == 70 && tm.tm_hour == -12 && tm.tm_min == 0 && tm.tm_sec == 0, "got %d, %d, %d, %d.\n", + tm.tm_year, tm.tm_hour, tm.tm_min, tm.tm_sec); + + t = -43201; + ptm = p_gmtime64(&t); + ok(!ptm, "got non-NULL.\n"); + memset(&tm, 0xcc, sizeof(tm)); + ret = p_gmtime64_s(&tm, &t); + ok(ret == EINVAL, "got %d.\n", ret); + ok(tm.tm_year == -1 && tm.tm_hour == -1 && tm.tm_min == -1 && tm.tm_sec == -1, "got %d, %d, %d, %d.\n", + tm.tm_year, tm.tm_hour, tm.tm_min, tm.tm_sec); + ptm = p_gmtime32((__time32_t *)&t); + ok(!ptm, "got NULL.\n"); + memset(&tm, 0xcc, sizeof(tm)); + ret = p_gmtime32_s(&tm, (__time32_t *)&t); + ok(ret == EINVAL, "got %d.\n", ret); + ok(tm.tm_year == -1 && tm.tm_hour == -1 && tm.tm_min == -1 && tm.tm_sec == -1, "got %d, %d, %d, %d.\n", + tm.tm_year, tm.tm_hour, tm.tm_min, tm.tm_sec); + + t = _MAX__TIME64_T + 46800; + memset(&tm, 0xcc, sizeof(tm)); + ptm = p_gmtime64(&t); + ok(!!ptm, "got NULL.\n"); + ret = p_gmtime64_s(&tm, &t); + ok(!ret, "got %d.\n", ret); + ok(tm.tm_year == 1101 && tm.tm_hour == 20 && tm.tm_min == 59 && tm.tm_sec == 59, "got %d, %d, %d, %d.\n", + tm.tm_year, tm.tm_hour, tm.tm_min, tm.tm_sec); + + t = _MAX__TIME64_T + 46801; + ptm = p_gmtime64(&t); + ok(!ptm, "got non-NULL.\n"); + memset(&tm, 0xcc, sizeof(tm)); + ret = p_gmtime64_s(&tm, &t); + ok(ret == EINVAL, "got %d.\n", ret); + ok(tm.tm_year == -1 && tm.tm_hour == -1 && tm.tm_min == -1 && tm.tm_sec == -1, "got %d, %d, %d, %d.\n", + tm.tm_year, tm.tm_hour, tm.tm_min, tm.tm_sec); +} + START_TEST(msvcr120) { if (!init()) return; @@ -1738,4 +1815,5 @@ START_TEST(msvcr120) test_CurrentContext(); test_StructuredTaskCollection(); test_strcmp(); + test_gmtime64(); } diff --git a/dlls/msvcrt/tests/time.c b/dlls/msvcrt/tests/time.c index 4ed17c940a9..aba911f30a4 100644 --- a/dlls/msvcrt/tests/time.c +++ b/dlls/msvcrt/tests/time.c @@ -55,6 +55,8 @@ static __time32_t (__cdecl *p_mkgmtime32)(struct tm*); static struct tm* (__cdecl *p_gmtime32)(__time32_t*); static struct tm* (__cdecl *p_gmtime)(time_t*); static errno_t (__cdecl *p_gmtime32_s)(struct tm*, __time32_t*); +static struct tm* (__cdecl *p_gmtime64)(__time64_t*); +static errno_t (__cdecl *p_gmtime64_s)(struct tm*, __time64_t*); static errno_t (__cdecl *p_strtime_s)(char*,size_t); static errno_t (__cdecl *p_strdate_s)(char*,size_t); static errno_t (__cdecl *p_localtime32_s)(struct tm*, __time32_t*); @@ -76,6 +78,8 @@ static void init(void) p_gmtime32 = (void*)GetProcAddress(hmod, "_gmtime32"); p_gmtime = (void*)GetProcAddress(hmod, "gmtime"); p_gmtime32_s = (void*)GetProcAddress(hmod, "_gmtime32_s"); + p_gmtime64 = (void*)GetProcAddress(hmod, "_gmtime64"); + p_gmtime64_s = (void*)GetProcAddress(hmod, "_gmtime64_s"); p_mkgmtime32 = (void*)GetProcAddress(hmod, "_mkgmtime32"); p_strtime_s = (void*)GetProcAddress(hmod, "_strtime_s"); p_strdate_s = (void*)GetProcAddress(hmod, "_strdate_s"); @@ -208,6 +212,78 @@ static void test_gmtime(void) } } +static void test_gmtime64(void) +{ + struct tm *ptm, tm; + __time64_t t; + int ret; + + t = -1; + memset(&tm, 0xcc, sizeof(tm)); + ptm = p_gmtime64(&t); + if (!ptm) + { + skip("Old gmtime64 limits, skipping tests.\n"); + return; + } + ok(!!ptm, "got NULL.\n"); + ret = p_gmtime64_s(&tm, &t); + ok(!ret, "got %d.\n", ret); + ok(tm.tm_year == 69 && tm.tm_hour == 23 && tm.tm_min == 59 && tm.tm_sec == 59, "got %d, %d, %d, %d.\n", + tm.tm_year, tm.tm_hour, tm.tm_min, tm.tm_sec); + + t = -43200; + memset(&tm, 0xcc, sizeof(tm)); + ptm = p_gmtime64(&t); + ok(!!ptm, "got NULL.\n"); + ret = p_gmtime64_s(&tm, &t); + ok(!ret, "got %d.\n", ret); + ok(tm.tm_year == 69 && tm.tm_hour == 12 && tm.tm_min == 0 && tm.tm_sec == 0, "got %d, %d, %d, %d.\n", + tm.tm_year, tm.tm_hour, tm.tm_min, tm.tm_sec); + ptm = p_gmtime32((__time32_t *)&t); + ok(!!ptm, "got NULL.\n"); + memset(&tm, 0xcc, sizeof(tm)); + ret = p_gmtime32_s(&tm, (__time32_t *)&t); + ok(!ret, "got %d.\n", ret); + todo_wine_if(tm.tm_year == 69 && tm.tm_hour == 12) + ok(tm.tm_year == 70 && tm.tm_hour == -12 && tm.tm_min == 0 && tm.tm_sec == 0, "got %d, %d, %d, %d.\n", + tm.tm_year, tm.tm_hour, tm.tm_min, tm.tm_sec); + + t = -43201; + ptm = p_gmtime64(&t); + ok(!ptm, "got non-NULL.\n"); + memset(&tm, 0xcc, sizeof(tm)); + ret = p_gmtime64_s(&tm, &t); + ok(ret == EINVAL, "got %d.\n", ret); + ok(tm.tm_year == -1 && tm.tm_hour == -1 && tm.tm_min == -1 && tm.tm_sec == -1, "got %d, %d, %d, %d.\n", + tm.tm_year, tm.tm_hour, tm.tm_min, tm.tm_sec); + ptm = p_gmtime32((__time32_t *)&t); + ok(!ptm, "got NULL.\n"); + memset(&tm, 0xcc, sizeof(tm)); + ret = p_gmtime32_s(&tm, (__time32_t *)&t); + ok(ret == EINVAL, "got %d.\n", ret); + ok(tm.tm_year == -1 && tm.tm_hour == -1 && tm.tm_min == -1 && tm.tm_sec == -1, "got %d, %d, %d, %d.\n", + tm.tm_year, tm.tm_hour, tm.tm_min, tm.tm_sec); + + t = _MAX__TIME64_T + 46800; + memset(&tm, 0xcc, sizeof(tm)); + ptm = p_gmtime64(&t); + ok(!!ptm, "got NULL.\n"); + ret = p_gmtime64_s(&tm, &t); + ok(!ret, "got %d.\n", ret); + ok(tm.tm_year == 1101 && tm.tm_hour == 20 && tm.tm_min == 59 && tm.tm_sec == 59, "got %d, %d, %d, %d.\n", + tm.tm_year, tm.tm_hour, tm.tm_min, tm.tm_sec); + + t = _MAX__TIME64_T + 46801; + ptm = p_gmtime64(&t); + ok(!ptm, "got non-NULL.\n"); + memset(&tm, 0xcc, sizeof(tm)); + ret = p_gmtime64_s(&tm, &t); + ok(ret == EINVAL, "got %d.\n", ret); + ok(tm.tm_year == -1 && tm.tm_hour == -1 && tm.tm_min == -1 && tm.tm_sec == -1, "got %d, %d, %d, %d.\n", + tm.tm_year, tm.tm_hour, tm.tm_min, tm.tm_sec); +} + static void test_mktime(void) { TIME_ZONE_INFORMATION tzinfo; @@ -963,6 +1039,7 @@ START_TEST(time) test_strftime(); test_ctime(); test_gmtime(); + test_gmtime64(); test_mktime(); test_localtime(); test_strdate(); diff --git a/dlls/msvcrt/time.c b/dlls/msvcrt/time.c index 56b80e84105..7a110e53455 100644 --- a/dlls/msvcrt/time.c +++ b/dlls/msvcrt/time.c @@ -66,6 +66,17 @@ static const int MAX_SECONDS = 60; static const int MAX_SECONDS = 59; #endif +#if _MSVCR_VER == 0 +#define MIN_GMTIME64_TIME 0 +#define MAX_GMTIME64_TIME _MAX__TIME64_T +#elif _MSVCR_VER >= 140 +#define MIN_GMTIME64_TIME -43200 +#define MAX_GMTIME64_TIME (_MAX__TIME64_T + 1605600) +#else +#define MIN_GMTIME64_TIME -43200 +#define MAX_GMTIME64_TIME (_MAX__TIME64_T + 46800) +#endif + static inline BOOL IsLeapYear(int Year) { return Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0); @@ -457,7 +468,9 @@ int CDECL _gmtime64_s(struct tm *res, const __time64_t *secs) SYSTEMTIME st; ULONGLONG time; - if (!res || !secs || *secs < 0 || *secs > _MAX__TIME64_T) { + TRACE("res %p, secs %p (%I64d).\n", res, secs, secs ? *secs : 0); + + if (!res || !secs || *secs < MIN_GMTIME64_TIME || *secs > MAX_GMTIME64_TIME) { if (res) { write_invalid_msvcrt_tm(res); } diff --git a/dlls/ucrtbase/tests/misc.c b/dlls/ucrtbase/tests/misc.c index 86cdec88108..ff1ed20f2f8 100644 --- a/dlls/ucrtbase/tests/misc.c +++ b/dlls/ucrtbase/tests/misc.c @@ -113,6 +113,8 @@ _se_translator_function __cdecl _set_se_translator(_se_translator_function func) void** __cdecl __current_exception(void); int* __cdecl __processing_throw(void); +#define _MAX__TIME64_T (((__time64_t)0x00000007 << 32) | 0x93406FFF) + static void test__initialize_onexit_table(void) { _onexit_table_t table, table2; @@ -1632,6 +1634,78 @@ static void test_rewind_i386_abi(void) } #endif +static void test_gmtime64(void) +{ + struct tm *ptm, tm; + __time64_t t; + int ret; + + t = -1; + memset(&tm, 0xcc, sizeof(tm)); + ptm = _gmtime64(&t); + ok(!!ptm, "got NULL.\n"); + ret = _gmtime64_s(&tm, &t); + ok(!ret, "got %d.\n", ret); + ok(tm.tm_year == 69 && tm.tm_hour == 23 && tm.tm_min == 59 && tm.tm_sec == 59, "got %d, %d, %d, %d.\n", + tm.tm_year, tm.tm_hour, tm.tm_min, tm.tm_sec); + + t = -43200; + memset(&tm, 0xcc, sizeof(tm)); + ptm = _gmtime64(&t); + ok(!!ptm, "got NULL.\n"); + ret = _gmtime64_s(&tm, &t); + ok(!ret, "got %d.\n", ret); + ok(tm.tm_year == 69 && tm.tm_hour == 12 && tm.tm_min == 0 && tm.tm_sec == 0, "got %d, %d, %d, %d.\n", + tm.tm_year, tm.tm_hour, tm.tm_min, tm.tm_sec); + ptm = _gmtime32((__time32_t *)&t); + ok(!!ptm, "got NULL.\n"); + memset(&tm, 0xcc, sizeof(tm)); + ret = _gmtime32_s(&tm, (__time32_t *)&t); + ok(!ret, "got %d.\n", ret); + todo_wine_if(tm.tm_year == 69 && tm.tm_hour == 12) + ok(tm.tm_year == 70 && tm.tm_hour == -12 && tm.tm_min == 0 && tm.tm_sec == 0, "got %d, %d, %d, %d.\n", + tm.tm_year, tm.tm_hour, tm.tm_min, tm.tm_sec); + + t = -43201; + ptm = _gmtime64(&t); + ok(!ptm, "got non-NULL.\n"); + memset(&tm, 0xcc, sizeof(tm)); + ret = _gmtime64_s(&tm, &t); + ok(ret == EINVAL, "got %d.\n", ret); + ok(tm.tm_year == -1 && tm.tm_hour == -1 && tm.tm_min == -1 && tm.tm_sec == -1, "got %d, %d, %d, %d.\n", + tm.tm_year, tm.tm_hour, tm.tm_min, tm.tm_sec); + ptm = _gmtime32((__time32_t *)&t); + ok(!ptm, "got NULL.\n"); + memset(&tm, 0xcc, sizeof(tm)); + ret = _gmtime32_s(&tm, (__time32_t *)&t); + ok(ret == EINVAL, "got %d.\n", ret); + ok(tm.tm_year == -1 && tm.tm_hour == -1 && tm.tm_min == -1 && tm.tm_sec == -1, "got %d, %d, %d, %d.\n", + tm.tm_year, tm.tm_hour, tm.tm_min, tm.tm_sec); + + t = _MAX__TIME64_T + 1605600; + memset(&tm, 0xcc, sizeof(tm)); + ptm = _gmtime64(&t); + ok(!!ptm || broken(!ptm) /* before Win10 1909 */, "got NULL.\n"); + if (!ptm) + { + win_skip("Old gmtime64 limits, skipping tests.\n"); + return; + } + ret = _gmtime64_s(&tm, &t); + ok(!ret, "got %d.\n", ret); + ok(tm.tm_year == 1101 && tm.tm_hour == 21 && tm.tm_min == 59 && tm.tm_sec == 59, "got %d, %d, %d, %d.\n", + tm.tm_year, tm.tm_hour, tm.tm_min, tm.tm_sec); + + t = _MAX__TIME64_T + 1605601; + ptm = _gmtime64(&t); + ok(!ptm, "got non-NULL.\n"); + memset(&tm, 0xcc, sizeof(tm)); + ret = _gmtime64_s(&tm, &t); + ok(ret == EINVAL, "got %d.\n", ret); + ok(tm.tm_year == -1 && tm.tm_hour == -1 && tm.tm_min == -1 && tm.tm_sec == -1, "got %d, %d, %d, %d.\n", + tm.tm_year, tm.tm_hour, tm.tm_min, tm.tm_sec); +} + START_TEST(misc) { int arg_c; @@ -1677,4 +1751,5 @@ START_TEST(misc) #if defined(__i386__) test_rewind_i386_abi(); #endif + test_gmtime64(); } diff --git a/include/msvcrt/time.h b/include/msvcrt/time.h index e1d914fea0d..4d79d066184 100644 --- a/include/msvcrt/time.h +++ b/include/msvcrt/time.h @@ -80,7 +80,10 @@ _ACRTIMP errno_t __cdecl _ctime64_s(char*,size_t,const __time64_t*); _ACRTIMP double __cdecl _difftime32(__time32_t,__time32_t); _ACRTIMP double __cdecl _difftime64(__time64_t,__time64_t); _ACRTIMP struct tm* __cdecl _gmtime32(const __time32_t*); +_ACRTIMP int __cdecl _gmtime32_s(struct tm *res, const __time32_t *secs); _ACRTIMP struct tm* __cdecl _gmtime64(const __time64_t*); +_ACRTIMP int __cdecl _gmtime64_s(struct tm *res, const __time64_t *secs); + _ACRTIMP struct tm* __cdecl _localtime32(const __time32_t*); _ACRTIMP errno_t __cdecl _localtime32_s(struct tm*, const __time32_t*); _ACRTIMP struct tm* __cdecl _localtime64(const __time64_t*);