commit 49ac3a16e919ff53299c672c8c641c421f5134f6 Author: Jacob Welsh AuthorDate: Sat Nov 12 03:48:01 2022 +0000 Commit: Jacob Welsh CommitDate: Sat Nov 12 03:48:01 2022 +0000 Type: cleanup lib-imap, lib-mail, lib-storage, lib: cut last uses of the magic TIME_T_MAX_BITS, tightening date/time parse error handling. The core is that date/time decoding errors in imap_mktime, whether due to time_t overflow or just having a component out of range, are no longer clipped or projected to arbitrary pseudo-maximum values but set to (time_t)-1, which was already an error signal per utc_mktime/timegm. For consistency (and since time_t isn't even necessarily signed), any time before the epoch is now considered underflow. The rest is adjustment of calling code (imap_mktime is consumed directly only by imap_parse_date/imap_parse_datetime), typically to make it notice the error. Behavior of the IMAP APPEND command is unchanged: an invalid internal date argument will be ignored and the new message's internal date attribute set to the current time. Other behavioral changes can't be ruled out - mainly, searches now returning errors for the invalid date parameter instead of proceeding to possibly bogus results. http://fixpoint.welshcomputing.com/2022/jwrd-logs-for-Nov-2022/#5393 diff --git a/src/lib-imap/imap-date.c b/src/lib-imap/imap-date.c index 2e5569a510..bf733129ea 100644 --- a/src/lib-imap/imap-date.c +++ b/src/lib-imap/imap-date.c @@ -82,26 +82,15 @@ static const char *imap_parse_date_internal(const char *str, struct tm *tm) static bool imap_mktime(struct tm *tm, time_t *time_r) { *time_r = utc_mktime(tm); + + /* Disallow all negatives since -1 is already special. No holes in the number line! */ + if (*time_r < 0) + *time_r = (time_t)-1; + if (*time_r != (time_t)-1) return TRUE; - /* the date is outside valid range for time_t. it might still be - technically valid though, so try to handle this case. - with 64bit time_t the full 0..9999 year range is valid. */ - if (tm->tm_year <= 100) { - /* too old. time_t can be signed or unsigned, handle - both cases. */ - *time_r = (time_t)-1 < (int)0 ? INT_MIN : 0; - } else { - /* too high. return the highest allowed value. - we shouldn't get here with 64bit time_t, - but handle that anyway. */ -#if (TIME_T_MAX_BITS == 32 || TIME_T_MAX_BITS == 64) - *time_r = (1UL << (TIME_T_MAX_BITS-1)) - 1; -#else - *time_r = (1UL << TIME_T_MAX_BITS) - 1; -#endif - } + /* The date is invalid (not normalized), or outside valid range for time_t. */ return FALSE; } diff --git a/src/lib-imap/imap-date.h b/src/lib-imap/imap-date.h index b8ea9827c0..7eaf5252d0 100644 --- a/src/lib-imap/imap-date.h +++ b/src/lib-imap/imap-date.h @@ -5,9 +5,7 @@ time_t is filled with UTC date. timezone_offset is filled with parsed timezone. If no timezone is given, local timezone is assumed. - If date is too low or too high to fit to time_t, it's set to lowest/highest - allowed value. This allows you to use the value directly for comparing - timestamps. */ + If date is too low or too high to fit to a nonnegative time_t, or is numerically invalid (day, hour etc. out of range), it's set to (time_t)-1 to signal the error, while still returning TRUE. */ bool imap_parse_date(const char *str, time_t *timestamp_r); bool imap_parse_datetime(const char *str, time_t *timestamp_r, int *timezone_offset_r); diff --git a/src/lib-mail/test-message-date.c b/src/lib-mail/test-message-date.c index b522ff22e0..777a0c9e84 100644 --- a/src/lib-mail/test-message-date.c +++ b/src/lib-mail/test-message-date.c @@ -18,9 +18,8 @@ static void test_message_date_parse(void) { "Thu, 01 Jan 1970 01:59:59 +0200", -1, 2*60, TRUE }, { "Fri, 13 Dec 1901 20:45:53 +0000", -2147483647, 0, TRUE }, #endif -#if (TIME_T_MAX_BITS > 32 || !defined(TIME_T_SIGNED)) + /* Expected to fail on 32-bit signed time_t systems. Fix them! */ { "Sun, 07 Feb 2106 06:28:15 +0000", 4294967295U, 0, TRUE }, -#endif { "Wed, 07 Nov 2007 01:07:20 +0200", 1194390440, 2*60, TRUE }, { "Wed, 07 Nov 2007 01:07:20", 1194397640, 0, TRUE }, { "Thu, 01 Jan 1970 02:00:00 +0200", 0, 2*60, TRUE }, diff --git a/src/lib-storage/index/imapc/imapc-mail-fetch.c b/src/lib-storage/index/imapc/imapc-mail-fetch.c index 1b0eee7100..83478594ee 100644 --- a/src/lib-storage/index/imapc/imapc-mail-fetch.c +++ b/src/lib-storage/index/imapc/imapc-mail-fetch.c @@ -858,7 +858,8 @@ void imapc_mail_fetch_update(struct imapc_mail *mail, match = TRUE; } else if (strcasecmp(key, "INTERNALDATE") == 0) { if (imap_arg_get_astring(&args[i+1], &value) && - imap_parse_datetime(value, &t, &tz)) { + imap_parse_datetime(value, &t, &tz) && + t != (time_t)-1) { mail->imail.data.received_date = t; if (HAS_NO_BITS(mbox->capabilities, IMAPC_CAPABILITY_SAVEDATE)) @@ -869,7 +870,8 @@ void imapc_mail_fetch_update(struct imapc_mail *mail, if (imap_arg_get_astring(&args[i+1], &value)) { if (strcasecmp(value, "NIL") == 0) mail->imail.data.save_date = 0; - else if (imap_parse_datetime(value, &t, &tz)) + else if (imap_parse_datetime(value, &t, &tz) + && t != (time_t)-1) mail->imail.data.save_date = t; } match = TRUE; diff --git a/src/lib-storage/mail-search-mime-register.c b/src/lib-storage/mail-search-mime-register.c index c0bda3d2a3..57ac21f239 100644 --- a/src/lib-storage/mail-search-mime-register.c +++ b/src/lib-storage/mail-search-mime-register.c @@ -162,7 +162,8 @@ arg_new_date(struct mail_search_mime_build_context *ctx, smarg = mail_search_mime_build_new(ctx, type); if (mail_search_parse_string(ctx->ctx->parser, &value) < 0) return NULL; - if (!imap_parse_date(value, &smarg->value.time)) { + if (!imap_parse_date(value, &smarg->value.time) + || smarg->value.time == (time_t)-1) { ctx->ctx->_error = "Invalid search date parameter"; return NULL; } diff --git a/src/lib-storage/mail-search-register-imap.c b/src/lib-storage/mail-search-register-imap.c index c888b9e195..0495093be7 100644 --- a/src/lib-storage/mail-search-register-imap.c +++ b/src/lib-storage/mail-search-register-imap.c @@ -165,7 +165,8 @@ arg_new_date(struct mail_search_build_context *ctx, sarg = mail_search_build_new(ctx, type); if (mail_search_parse_string(ctx->parser, &value) < 0) return NULL; - if (!imap_parse_date(value, &sarg->value.time)) { + if (!imap_parse_date(value, &sarg->value.time) + || sarg->value.time == (time_t)-1) { ctx->_error = "Invalid search date parameter"; return NULL; } diff --git a/src/lib-storage/mail-storage.c b/src/lib-storage/mail-storage.c index 0a71426871..8cc03a3fea 100644 --- a/src/lib-storage/mail-storage.c +++ b/src/lib-storage/mail-storage.c @@ -3146,7 +3146,7 @@ int mail_parse_human_timestamp(const char *str, time_t *timestamp_r, } else if (imap_parse_date(str, timestamp_r)) { /* imap date */ *utc_r = FALSE; - return 0; + return *timestamp_r == (time_t)-1 ? -1 : 0; } else if (str_to_time(str, timestamp_r) == 0) { /* unix timestamp */ *utc_r = TRUE; diff --git a/src/lib/test-utc-mktime.c b/src/lib/test-utc-mktime.c index 157179ba2f..f5a37b23c9 100644 --- a/src/lib/test-utc-mktime.c +++ b/src/lib/test-utc-mktime.c @@ -15,17 +15,14 @@ void test_utc_mktime(void) { 1969, 12, 31, 23, 59, 59, -1 }, { 1901, 12, 13, 20, 45, 53, -2147483647 }, #endif -#if (TIME_T_MAX_BITS > 32 || !defined(TIME_T_SIGNED)) + /* Expected to fail on 32-bit signed time_t systems. Fix them! */ { 2106, 2, 7, 6, 28, 15, 4294967295 }, + { 2106, 2, 7, 6, 28, 16, 4294967296 }, { 2038, 1, 19, 3, 14, 8, 2147483648 }, -#endif { 2007, 11, 7, 1, 7, 20, 1194397640 }, { 1970, 1, 1, 0, 0, 0, 0 }, { 2038, 1, 19, 3, 14, 7, 2147483647 }, { INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, -1 }, -#if TIME_T_MAX_BITS > 32 - { 2106, 2, 7, 6, 28, 16, 4294967296 }, -#endif /* June leap second */ { 2015, 6, 30, 23, 59, 59, 1435708799 }, { 2015, 6, 30, 23, 59, 60, 1435708799 },