From 00ffb79c529942eab5c81568808bd317c753213a Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Wed, 11 Nov 2020 19:16:23 -0800 Subject: [PATCH 2/3] parse-datetime: streamline overflow checking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When parse-datetime.y’s overflow code was written, INT_ADD_WRAPV did not work for unsigned destinations, and since time_t might be unsigned that meant it did not work for time_t destinations. This limitation of INT_ADD_WRAPV has been fixed, so we can now streamline parse-datetime.y a bit. * lib/parse-datetime.y: Do not include limits.h, as LONG_MAX has not been used for a while. (yylex, parse_datetime2): Assume C99 declarations after statements. (yyles): Use INT_SUBTRACT_WRAPV instead of an explicit comparison to TYPE_MINIMUM. (parse_datetime2): No need for time_overflow now that INT_ADD_WRAPV works for unsigned results. --- ChangeLog | 14 ++++++++++++++ lib/parse-datetime.y | 41 ++++++++++++++++------------------------- 2 files changed, 30 insertions(+), 25 deletions(-) diff --git a/ChangeLog b/ChangeLog index e1828df64..530c9a661 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,19 @@ 2020-11-11 Paul Eggert + parse-datetime: streamline overflow checking + When parse-datetime.y’s overflow code was written, INT_ADD_WRAPV + did not work for unsigned destinations, and since time_t might + be unsigned that meant it did not work for time_t destinations. + This limitation of INT_ADD_WRAPV has been fixed, so we can + now streamline parse-datetime.y a bit. + * lib/parse-datetime.y: Do not include limits.h, as LONG_MAX + has not been used for a while. + (yylex, parse_datetime2): Assume C99 declarations after statements. + (yyles): Use INT_SUBTRACT_WRAPV instead of an explicit comparison + to TYPE_MINIMUM. + (parse_datetime2): No need for time_overflow now that + INT_ADD_WRAPV works for unsigned results. + parse-datetime-tests: port to Alpine Linux 3.12.1 * tests/test-parse-datetime.c: Include errno.h for errno, and unistd.h for _SC_TZNAME_MAX and sysconf. diff --git a/lib/parse-datetime.y b/lib/parse-datetime.y index 0c6246742..44ae90350 100644 --- a/lib/parse-datetime.y +++ b/lib/parse-datetime.y @@ -27,7 +27,7 @@ Modified by Paul Eggert in 1999 to do the right thing about local DST. Also modified by Paul Eggert in 2004 to support nanosecond-resolution - timestamps, in 2004 to support TZ strings in dates, and in 2017 to + timestamps, in 2004 to support TZ strings in dates, and in 2017 and 2020 to check for integer overflow and to support longer-than-'long' 'time_t' and 'tv_nsec'. */ @@ -63,7 +63,6 @@ #include #include -#include #include #include #include @@ -1410,13 +1409,12 @@ yylex (union YYSTYPE *lvalp, parser_control *pc) if (c_isdigit (c) || c == '-' || c == '+') { - char const *p; + char const *p = pc->input; int sign; - intmax_t value = 0; if (c == '-' || c == '+') { sign = c == '-' ? -1 : 1; - while (c = *++pc->input, c_isspace (c)) + while (c = *(pc->input = ++p), c_isspace (c)) continue; if (! c_isdigit (c)) /* skip the '-' sign */ @@ -1424,8 +1422,8 @@ yylex (union YYSTYPE *lvalp, parser_control *pc) } else sign = 0; - p = pc->input; + time_t value = 0; do { if (INT_MULTIPLY_WRAPV (value, 10, &value)) @@ -1438,17 +1436,12 @@ yylex (union YYSTYPE *lvalp, parser_control *pc) if ((c == '.' || c == ',') && c_isdigit (p[1])) { - time_t s; - int ns; + time_t s = value; int digits; - if (time_overflow (value)) - return '?'; - s = value; - /* Accumulate fraction, to ns precision. */ p++; - ns = *p++ - '0'; + int ns = *p++ - '0'; for (digits = 2; digits <= LOG10_BILLION; digits++) { ns *= 10; @@ -1472,9 +1465,8 @@ yylex (union YYSTYPE *lvalp, parser_control *pc) negative. */ if (sign < 0 && ns) { - if (s == TYPE_MINIMUM (time_t)) + if (INT_SUBTRACT_WRAPV (s, 1, &s)) return '?'; - s--; ns = BILLION - ns; } @@ -1857,11 +1849,9 @@ parse_datetime2 (struct timespec *result, char const *p, int quarter; for (quarter = 1; quarter <= 3; quarter++) { - intmax_t iprobe; - if (INT_ADD_WRAPV (Start, quarter * (90 * 24 * 60 * 60), &iprobe) - || time_overflow (iprobe)) + time_t probe; + if (INT_ADD_WRAPV (Start, quarter * (90 * 24 * 60 * 60), &probe)) break; - time_t probe = iprobe; struct tm probe_tm; if (localtime_rz (tz, &probe, &probe_tm) && probe_tm.tm_zone && probe_tm.tm_isdst != pc.local_time_zone_table[0].value) @@ -2237,7 +2227,6 @@ parse_datetime2 (struct timespec *result, char const *p, so this block must follow others that clobber Start. */ if (pc.zones_seen) { - intmax_t delta = pc.time_zone, t1; bool overflow = false; #ifdef HAVE_TM_GMTOFF long int utcoff = tm.tm_gmtoff; @@ -2248,9 +2237,11 @@ parse_datetime2 (struct timespec *result, char const *p, ? tm_diff (&tm, &gmt) : (overflow = true, 0)); #endif - overflow |= INT_SUBTRACT_WRAPV (delta, utcoff, &delta); + intmax_t delta; + overflow |= INT_SUBTRACT_WRAPV (pc.time_zone, utcoff, &delta); + time_t t1; overflow |= INT_SUBTRACT_WRAPV (Start, delta, &t1); - if (overflow || time_overflow (t1)) + if (overflow) { if (pc.parse_datetime_debug) dbg_printf (_("error: timezone %d caused time_t overflow\n"), @@ -2281,14 +2272,14 @@ parse_datetime2 (struct timespec *result, char const *p, intmax_t sum_ns = orig_ns + pc.rel.ns; int normalized_ns = (sum_ns % BILLION + BILLION) % BILLION; int d4 = (sum_ns - normalized_ns) / BILLION; - intmax_t d1, t1, d2, t2, t3, t4; + intmax_t d1, t1, d2, t2, t3; + time_t t4; if (INT_MULTIPLY_WRAPV (pc.rel.hour, 60 * 60, &d1) || INT_ADD_WRAPV (Start, d1, &t1) || INT_MULTIPLY_WRAPV (pc.rel.minutes, 60, &d2) || INT_ADD_WRAPV (t1, d2, &t2) || INT_ADD_WRAPV (t2, pc.rel.seconds, &t3) - || INT_ADD_WRAPV (t3, d4, &t4) - || time_overflow (t4)) + || INT_ADD_WRAPV (t3, d4, &t4)) { if (pc.parse_datetime_debug) dbg_printf (_("error: adding relative time caused an " -- 2.25.1