[libc-commits] [libc] 81d8273 - [libc] Fix overflow check for 32-bit mktime. (#101993)
via libc-commits
libc-commits at lists.llvm.org
Tue Aug 6 20:51:29 PDT 2024
Author: Simon Tatham
Date: 2024-08-06T20:51:25-07:00
New Revision: 81d8273978ace8b9a2b8cfd811b94e95f9560ac5
URL: https://github.com/llvm/llvm-project/commit/81d8273978ace8b9a2b8cfd811b94e95f9560ac5
DIFF: https://github.com/llvm/llvm-project/commit/81d8273978ace8b9a2b8cfd811b94e95f9560ac5.diff
LOG: [libc] Fix overflow check for 32-bit mktime. (#101993)
The 32-bit time_t rolls over to a value with its high bit set at
2038-01-19 03:14:07. The overflow check for this in mktime() was
checking each individual component of the time: reject if year>2038, or
if month>1, or if day>19, etc. As a result it would reject valid times
before the overflow point, because a low-order component was out of
range even though a higher-order one makes the time as a whole safe. The
earliest failing value is 2145916808 == 2038-01-01 00:00:08, in which
only the seconds field 'overflows'.
Fixed so that if any component is _less_ than its threshold value, we
don't check the lower-order components.
Added:
Modified:
libc/src/time/mktime.cpp
libc/test/src/time/mktime_test.cpp
Removed:
################################################################################
diff --git a/libc/src/time/mktime.cpp b/libc/src/time/mktime.cpp
index 9ea316d504f5b..72cd229120538 100644
--- a/libc/src/time/mktime.cpp
+++ b/libc/src/time/mktime.cpp
@@ -42,12 +42,18 @@ LLVM_LIBC_FUNCTION(time_t, mktime, (struct tm * tm_out)) {
return time_utils::out_of_range();
if (tm_out->tm_mday > 19)
return time_utils::out_of_range();
- if (tm_out->tm_hour > 3)
- return time_utils::out_of_range();
- if (tm_out->tm_min > 14)
- return time_utils::out_of_range();
- if (tm_out->tm_sec > 7)
- return time_utils::out_of_range();
+ else if (tm_out->tm_mday == 19) {
+ if (tm_out->tm_hour > 3)
+ return time_utils::out_of_range();
+ else if (tm_out->tm_hour == 3) {
+ if (tm_out->tm_min > 14)
+ return time_utils::out_of_range();
+ else if (tm_out->tm_min == 14) {
+ if (tm_out->tm_sec > 7)
+ return time_utils::out_of_range();
+ }
+ }
+ }
}
// Years are ints. A 32-bit year will fit into a 64-bit time_t.
diff --git a/libc/test/src/time/mktime_test.cpp b/libc/test/src/time/mktime_test.cpp
index 3142a4f3d06dd..84e6c7eb2c42e 100644
--- a/libc/test/src/time/mktime_test.cpp
+++ b/libc/test/src/time/mktime_test.cpp
@@ -380,22 +380,115 @@ TEST(LlvmLibcMkTime, InvalidDays) {
TEST(LlvmLibcMkTime, EndOf32BitEpochYear) {
// Test for maximum value of a signed 32-bit integer.
// Test implementation can encode time for Tue 19 January 2038 03:14:07 UTC.
- struct tm tm_data {
- .tm_sec = 7, .tm_min = 14, .tm_hour = 3, .tm_mday = 19,
- .tm_mon = Month::JANUARY, .tm_year = tm_year(2038), .tm_wday = 0,
- .tm_yday = 0, .tm_isdst = 0
- };
- EXPECT_THAT(LIBC_NAMESPACE::mktime(&tm_data), Succeeds(0x7FFFFFFF));
- EXPECT_TM_EQ((tm{.tm_sec = 7,
- .tm_min = 14,
- .tm_hour = 3,
- .tm_mday = 19,
- .tm_mon = Month::JANUARY,
- .tm_year = tm_year(2038),
- .tm_wday = 2,
- .tm_yday = 7,
- .tm_isdst = 0}),
- tm_data);
+ {
+ struct tm tm_data {
+ .tm_sec = 7, .tm_min = 14, .tm_hour = 3, .tm_mday = 19,
+ .tm_mon = Month::JANUARY, .tm_year = tm_year(2038), .tm_wday = 0,
+ .tm_yday = 0, .tm_isdst = 0
+ };
+ EXPECT_THAT(LIBC_NAMESPACE::mktime(&tm_data), Succeeds(0x7FFFFFFF));
+ EXPECT_TM_EQ((tm{.tm_sec = 7,
+ .tm_min = 14,
+ .tm_hour = 3,
+ .tm_mday = 19,
+ .tm_mon = Month::JANUARY,
+ .tm_year = tm_year(2038),
+ .tm_wday = 2,
+ .tm_yday = 7,
+ .tm_isdst = 0}),
+ tm_data);
+ }
+
+ // Now test some times before that, to ensure they are not rejected.
+ {
+ // 2038-01-19 03:13:59 tests that even a large seconds field is
+ // accepted if the minutes field is smaller.
+ struct tm tm_data {
+ .tm_sec = 59, .tm_min = 13, .tm_hour = 3, .tm_mday = 19,
+ .tm_mon = Month::JANUARY, .tm_year = tm_year(2038), .tm_wday = 0,
+ .tm_yday = 0, .tm_isdst = 0
+ };
+ EXPECT_THAT(LIBC_NAMESPACE::mktime(&tm_data), Succeeds(0x7FFFFFFF - 8));
+ EXPECT_TM_EQ((tm{.tm_sec = 59,
+ .tm_min = 13,
+ .tm_hour = 3,
+ .tm_mday = 19,
+ .tm_mon = Month::JANUARY,
+ .tm_year = tm_year(2038),
+ .tm_wday = 2,
+ .tm_yday = 7,
+ .tm_isdst = 0}),
+ tm_data);
+ }
+
+ {
+ // 2038-01-19 02:59:59 tests that large seconds and minutes are
+ // accepted if the hours field is smaller.
+ struct tm tm_data {
+ .tm_sec = 59, .tm_min = 59, .tm_hour = 2, .tm_mday = 19,
+ .tm_mon = Month::JANUARY, .tm_year = tm_year(2038), .tm_wday = 0,
+ .tm_yday = 0, .tm_isdst = 0
+ };
+ EXPECT_THAT(LIBC_NAMESPACE::mktime(&tm_data),
+ Succeeds(0x7FFFFFFF - 8 - 14 * TimeConstants::SECONDS_PER_MIN));
+ EXPECT_TM_EQ((tm{.tm_sec = 59,
+ .tm_min = 59,
+ .tm_hour = 2,
+ .tm_mday = 19,
+ .tm_mon = Month::JANUARY,
+ .tm_year = tm_year(2038),
+ .tm_wday = 2,
+ .tm_yday = 7,
+ .tm_isdst = 0}),
+ tm_data);
+ }
+
+ {
+ // 2038-01-18 23:59:59 tests that large seconds, minutes and hours
+ // are accepted if the days field is smaller.
+ struct tm tm_data {
+ .tm_sec = 59, .tm_min = 59, .tm_hour = 23, .tm_mday = 18,
+ .tm_mon = Month::JANUARY, .tm_year = tm_year(2038), .tm_wday = 0,
+ .tm_yday = 0, .tm_isdst = 0
+ };
+ EXPECT_THAT(LIBC_NAMESPACE::mktime(&tm_data),
+ Succeeds(0x7FFFFFFF - 8 - 14 * TimeConstants::SECONDS_PER_MIN -
+ 3 * TimeConstants::SECONDS_PER_HOUR));
+ EXPECT_TM_EQ((tm{.tm_sec = 59,
+ .tm_min = 59,
+ .tm_hour = 23,
+ .tm_mday = 18,
+ .tm_mon = Month::JANUARY,
+ .tm_year = tm_year(2038),
+ .tm_wday = 2,
+ .tm_yday = 7,
+ .tm_isdst = 0}),
+ tm_data);
+ }
+
+ {
+ // 2038-01-18 23:59:59 tests that the final second of 2037 is
+ // accepted.
+ struct tm tm_data {
+ .tm_sec = 59, .tm_min = 59, .tm_hour = 23, .tm_mday = 31,
+ .tm_mon = Month::DECEMBER, .tm_year = tm_year(2037), .tm_wday = 0,
+ .tm_yday = 0, .tm_isdst = 0
+ };
+ EXPECT_THAT(LIBC_NAMESPACE::mktime(&tm_data),
+ Succeeds(0x7FFFFFFF - 8 - 14 * TimeConstants::SECONDS_PER_MIN -
+ 3 * TimeConstants::SECONDS_PER_HOUR -
+ 18 * TimeConstants::SECONDS_PER_DAY));
+ EXPECT_TM_EQ((tm{.tm_sec = 59,
+ .tm_min = 59,
+ .tm_hour = 23,
+ .tm_mday = 31,
+ .tm_mon = Month::DECEMBER,
+ .tm_year = tm_year(2037),
+ .tm_wday = 2,
+ .tm_yday = 7,
+ .tm_isdst = 0}),
+ tm_data);
+ }
}
TEST(LlvmLibcMkTime, Max64BitYear) {
More information about the libc-commits
mailing list