From 480d6bd80af3f32737400bc57a277a2210ef1797 Mon Sep 17 00:00:00 2001 From: J William Piggott Date: Wed, 14 Jun 2017 19:51:30 -0400 Subject: [PATCH] parse-date: time_zone_hhmm() bug fixes * fix incorrect arg type used for abs(). * prevent integer arithmetic overflow by limiting offset to 4 digits. * don't accept malformed offset values like: -4:3 +12:719 +0000001:23 -9:00000001 +0000001123 Reported-by: Ruediger Meier Influenced-by: gnulib 30784c4 Paul Eggert Signed-off-by: J William Piggott --- lib/parse-date.y | 66 +++++++++++++++++++++++------------------------- 1 file changed, 32 insertions(+), 34 deletions(-) diff --git a/lib/parse-date.y b/lib/parse-date.y index c06616ab8..5e966f0c3 100644 --- a/lib/parse-date.y +++ b/lib/parse-date.y @@ -214,7 +214,7 @@ typedef struct { union YYSTYPE; static int yylex (union YYSTYPE *, parser_control *); static int yyerror (parser_control const *, char const *); -static long int time_zone_hhmm (parser_control *, textint, intmax_t); +static int time_zone_hhmm (parser_control *, textint, textint); /** * Extract into *PC any date and time info from a string of digits @@ -307,7 +307,7 @@ set_hhmmss(parser_control *pc, intmax_t hour, intmax_t minutes, %token tSNUMBER tUNUMBER %token tSDECIMAL_NUMBER tUDECIMAL_NUMBER -%type o_colon_minutes +%type o_colon_minutes %type seconds signed_seconds unsigned_seconds %type relunit relunit_snumber dayshift @@ -402,7 +402,7 @@ o_zone_offset: zone_offset: tSNUMBER o_colon_minutes { pc->zones_seen++; - pc->time_zone = time_zone_hhmm (pc, $1, $2); + if (! time_zone_hhmm (pc, $1, $2)) YYABORT; } ; @@ -455,7 +455,8 @@ zone: apply_relative_time (pc, $2, 1); } | tZONE tSNUMBER o_colon_minutes { - pc->time_zone = $1 + time_zone_hhmm (pc, $2, $3); + if (! time_zone_hhmm (pc, $2, $3)) YYABORT; + pc->time_zone += $1; } | tDAYZONE { pc->time_zone = $1 + 60; @@ -662,9 +663,10 @@ hybrid: o_colon_minutes: /* empty */ - { $$ = -1; } - | ':' tUNUMBER - { $$ = $2.value; } + { $$.value = $$.digits = 0; } + | ':' tUNUMBER { + $$ = $2; + } ; %% @@ -855,39 +857,35 @@ static table const military_table[] = { }; /** - * Convert a time zone expressed as HH:MM into an integer count of - * minutes. If MM is negative, then S is of the form HHMM and needs - * to be picked apart; otherwise, S is of the form HH. As specified in - * http://www.opengroup.org/susv3xbd/xbd_chap08.html#tag_08_03, allow - * only valid TZ range, and consider first two digits as hours, if no - * minutes specified. + * Convert a time offset expressed as HH:MM or HHMM into an integer count of + * minutes. If hh is more than 2 digits then it is of the form HHMM and must be + * delimited; in that case 'mm' is required to be absent. Otherwise, hh and mm + * are used ('mm' contains digits that were prefixed with a colon). + * + * POSIX TZ and ISO 8601 both define the maximum offset as 24:59. POSIX also + * allows seconds, but currently the parser rejects them. Both require minutes + * to be zero padded (2 digits). ISO requires hours to be zero padded, POSIX + * does not, either is accepted; which means an invalid ISO offset could pass. */ -static long int time_zone_hhmm(parser_control *pc, textint s, intmax_t mm) +static int time_zone_hhmm(parser_control *pc, textint hh, textint mm) { - intmax_t n_minutes; + int h, m; - /** - * If the length of S is 1 or 2 and no minutes are specified, - * interpret it as a number of hours. - */ - if (s.digits <= 2 && mm < 0) - s.value *= 100; + if (hh.digits > 2 && hh.digits < 5 && mm.digits == 0) { + h = hh.value / 100; + m = hh.value % 100; + } else if (hh.digits < 3 && (mm.digits == 0 || mm.digits == 2)) { + h = hh.value; + m = hh.negative ? -mm.value : mm.value; + } else + return 0; - if (mm < 0) - n_minutes = (s.value / 100) * 60 + s.value % 100; - else - n_minutes = s.value * 60 + (s.negative ? -mm : mm); + if (abs(h) > 24 || abs(m) > 59) + return 0; - /** - * If the absolute number of minutes is larger than 24 hours, - * arrange to reject it by incrementing pc->zones_seen. Thus, - * we allow only values in the range UTC-24:00 to UTC+24:00. - */ - if (24 * 60 < abs (n_minutes)) - pc->zones_seen++; - - return n_minutes; + pc->time_zone = h * 60 + m; + return 1; } static int to_hour(intmax_t hours, int meridian)