Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/lib/libutil PR lib/46542



details:   https://anonhg.NetBSD.org/src/rev/e7788e2646b9
branches:  trunk
changeset: 945456:e7788e2646b9
user:      kre <kre%NetBSD.org@localhost>
date:      Fri Oct 30 22:03:11 2020 +0000

description:
PR lib/46542

Add checks to detect overflow, and also detect other invalid
(out of range) inputs for parsedate().

There could be more, and some of what is being added is not
perfect, but many calculation overflows will be detected now
(and cause an error return) and some of the most bizarre
inputs that were previously accepted no longer will be.

diffstat:

 lib/libutil/parsedate.y |  157 ++++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 139 insertions(+), 18 deletions(-)

diffs (truncated from 344 to 300 lines):

diff -r d0754663cc12 -r e7788e2646b9 lib/libutil/parsedate.y
--- a/lib/libutil/parsedate.y   Fri Oct 30 21:44:49 2020 +0000
+++ b/lib/libutil/parsedate.y   Fri Oct 30 22:03:11 2020 +0000
@@ -14,12 +14,13 @@
 
 #include <sys/cdefs.h>
 #ifdef __RCSID
-__RCSID("$NetBSD: parsedate.y,v 1.35 2020/10/19 17:47:45 kre Exp $");
+__RCSID("$NetBSD: parsedate.y,v 1.36 2020/10/30 22:03:11 kre Exp $");
 #endif
 
 #include <stdio.h>
 #include <ctype.h>
 #include <errno.h>
+#include <limits.h>
 #include <string.h>
 #include <time.h>
 #include <util.h>
@@ -95,6 +96,15 @@
                int     yyRelMonth;
        } yyRel[MAXREL];
 };
+
+static int RelVal(struct dateinfo *, time_t, time_t, int, int);
+
+#define CheckRelVal(a, b, c, d, e) do {                                \
+               if (!RelVal((a), (b), (c), (d), (e))) {         \
+                       YYREJECT;                               \
+               }                                               \
+       } while (0)
+
 %}
 
 %union {
@@ -191,6 +201,8 @@
 
 time:
          tUNUMBER tMERIDIAN {
+               if ($1 > 24)
+                       YYREJECT;
                param->yyMinutes = 0;
                param->yySeconds = 0;
                if ($2 == MER_NOON || $2 == MER_MN) {
@@ -209,6 +221,8 @@
                }
          }
        | tUNUMBER ':' tUNUMBER o_merid {
+               if ($1 > 24 || $3 >= 60)
+                       YYREJECT;
                param->yyMinutes = $3;
                param->yySeconds = 0;
                if ($4 == MER_NOON || $4 == MER_MN) {
@@ -227,6 +241,8 @@
                }
          }
        | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
+               if ($1 > 24 || $3 >= 60 || $5 > 60)
+                       YYREJECT;
                param->yyMinutes = $3;
                param->yySeconds = $5;
                if ($6 == MER_NOON || $6 == MER_MN) {
@@ -245,6 +261,8 @@
                }
          }
        | tUNUMBER ':' tUNUMBER ':' tUNUMBER '.' tUNUMBER {
+               if ($1 > 24 || $3 >= 60 || $5 > 60)
+                       YYREJECT;
                param->yyHour = $1;
                param->yyMinutes = $3;
                param->yySeconds = $5;
@@ -252,6 +270,8 @@
                /* XXX: Do nothing with fractional secs ($7) */
          }
        | tUNUMBER ':' tUNUMBER ':' tUNUMBER ',' tUNUMBER {
+               if ($1 > 24 || $3 >= 60 || $5 > 60)
+                       YYREJECT;
                param->yyHour = $1;
                param->yyMinutes = $3;
                param->yySeconds = $5;
@@ -280,6 +300,10 @@
 
 time_numericzone:
          tUNUMBER ':' tUNUMBER tSNUMBER {
+               if ($4 < -(47 * 100 + 59) || $4 > (47 * 100 + 59))
+                       YYREJECT;
+               if ($1 > 24 || $3 > 59)
+                       YYREJECT;
                param->yyHour = $1;
                param->yyMinutes = $3;
                param->yyMeridian = MER24;
@@ -287,6 +311,10 @@
                param->yyTimezone = - ($4 % 100 + ($4 / 100) * 60);
          }
        | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
+               if ($6 < -(47 * 100 + 59) || $6 > (47 * 100 + 59))
+                       YYREJECT;
+               if ($1 > 24 || $3 > 59 || $5 > 60)
+                       YYREJECT;
                param->yyHour = $1;
                param->yyMinutes = $3;
                param->yySeconds = $5;
@@ -303,6 +331,8 @@
        | tSNUMBER      {
                          if (param->yyHaveDate == 0 && param->yyHaveTime == 0)
                                YYREJECT;
+                         if ($1 < -(47 * 100 + 59) || $1 > (47 * 100 + 59))
+                               YYREJECT;
                          param->yyTimezone = - ($1 % 100 + ($1 / 100) * 60);
                          param->yyDSTmode = DSTmaybe;
                        }
@@ -316,15 +346,21 @@
 
 date:
          tUNUMBER '/' tUNUMBER {
+               if ($1 > 12 || $3 > 31 || $1 == 0 || $3 == 0)
+                       YYREJECT;
                param->yyMonth = $1;
                param->yyDay = $3;
          }
        | tUNUMBER '/' tUNUMBER '/' tUNUMBER {
                if ($1 >= 100) {
+                       if ($3 > 12 || $5 > 31 || $3 == 0 || $5 == 0)
+                               YYREJECT;
                        param->yyYear = $1;
                        param->yyMonth = $3;
                        param->yyDay = $5;
                } else {
+                       if ($1 >= 12 || $3 > 31 || $1 == 0 || $3 == 0)
+                               YYREJECT;
                        param->yyMonth = $1;
                        param->yyDay = $3;
                        param->yyYear = $5;
@@ -332,39 +368,55 @@
          }
        | tUNUMBER tSNUMBER tSNUMBER {
                /* ISO 8601 format.  yyyy-mm-dd.  */
+               if ($2 >= 0 || $2 < -12 || $3 >= 0 || $3 < -31)
+                       YYREJECT;
                param->yyYear = $1;
                param->yyHaveFullYear = 1;
                param->yyMonth = -$2;
                param->yyDay = -$3;
          }
        | tUNUMBER tMONTH tSNUMBER {
+               if ($3 > 0 || $1 == 0 || $1 > 31)
+                       YYREJECT;
                /* e.g. 17-JUN-1992.  */
                param->yyDay = $1;
                param->yyMonth = $2;
                param->yyYear = -$3;
          }
        | tMONTH tUNUMBER {
+               if ($2 == 0 || $2 > 31)
+                       YYREJECT;
                param->yyMonth = $1;
                param->yyDay = $2;
          }
        | tMONTH tUNUMBER ',' tUNUMBER {
+               if ($2 == 0 || $2 > 31)
+                       YYREJECT;
                param->yyMonth = $1;
                param->yyDay = $2;
                param->yyYear = $4;
          }
        | tUNUMBER tMONTH {
+               if ($1 == 0 || $1 > 31)
+                       YYREJECT;
                param->yyMonth = $2;
                param->yyDay = $1;
          }
        | tUNUMBER tMONTH tUNUMBER {
-               param->yyMonth = $2;
+               if ($1 > 31 && $3 > 31)
+                       YYREJECT;
                if ($1 < 35) {
+                       if ($1 == 0)
+                               YYREJECT;
                        param->yyDay = $1;
                        param->yyYear = $3;
                } else {
+                       if ($3 == 0)
+                               YYREJECT;
                        param->yyDay = $3;
                        param->yyYear = $1;
                }
+               param->yyMonth = $2;
          }
 ;
 
@@ -377,15 +429,15 @@
 ;
 
 relunit:
-         tUNUMBER tMINUTE_UNIT { RelVal(param, $1 * $2 * 60L, 0); }
-       | tSNUMBER tMINUTE_UNIT { RelVal(param, $1 * $2 * 60L, 0); }
-       | tMINUTE_UNIT          { RelVal(param, $1 * 60L, 0); }
-       | tSNUMBER tSEC_UNIT    { RelVal(param, $1, 0); }
-       | tUNUMBER tSEC_UNIT    { RelVal(param, $1, 0); }
-       | tSEC_UNIT             { RelVal(param, 1L, 0);  }
-       | tSNUMBER tMONTH_UNIT  { RelVal(param, $1 * $2, 1); }
-       | tUNUMBER tMONTH_UNIT  { RelVal(param, $1 * $2, 1); }
-       | tMONTH_UNIT           { RelVal(param, $1, 1); }
+         tUNUMBER tMINUTE_UNIT { CheckRelVal(param, $1, $2, 60, 0); }
+       | tSNUMBER tMINUTE_UNIT { CheckRelVal(param, $1, $2, 60, 0); }
+       | tMINUTE_UNIT          { CheckRelVal(param,  1, $1, 60, 0); }
+       | tSNUMBER tSEC_UNIT    { CheckRelVal(param, $1, 1,  1,  0); }
+       | tUNUMBER tSEC_UNIT    { CheckRelVal(param, $1, 1,  1,  0); }
+       | tSEC_UNIT             { CheckRelVal(param,  1, 1,  1,  0); }
+       | tSNUMBER tMONTH_UNIT  { CheckRelVal(param, $1, $2, 1,  1); }
+       | tUNUMBER tMONTH_UNIT  { CheckRelVal(param, $1, $2, 1,  1); }
+       | tMONTH_UNIT           { CheckRelVal(param,  1, $1, 1,  1); }
 ;
 
 number:
@@ -661,15 +713,46 @@
 /*
  * Save a relative value, if it fits
  */
-static void
-RelVal(struct dateinfo *param, time_t v, int type)
+static int
+RelVal(struct dateinfo *param, time_t num, time_t unit, int scale, int type)
 {
        int i;
+       time_t v;
+       uintmax_t m;
+       int sign = 1;
 
        if ((i = param->yyHaveRel) >= MAXREL)
-               return;
+               return 0;
+
+       if (num < 0) {
+               sign = -sign;
+               num = -num;
+       }
+       if (unit < 0) {
+               sign = -sign;
+               unit = -unit;
+       }
+       /* scale is always positive */
+
+       m = LLONG_MAX;          /* TIME_T_MAX */
+       if (scale > 1)
+               m /= scale;
+       if (unit > 1)
+               m /= unit;
+       if ((uintmax_t)num > m)
+               return 0;
+
+       m = num * unit * scale;
+       v = (time_t) m;
+       if (v < 0 || (uintmax_t)v != m)
+               return 0;
+       if (sign < 0)
+               v = -v;
+
        param->yyRel[i].yyRelMonth = type;
        param->yyRel[i].yyRelVal = v;
+
+       return 1;
 }
 
 /*
@@ -721,6 +804,10 @@
     tm.tm_mday = Day;
     tm.tm_mon = Month - 1;
     tm.tm_year = Year - 1900;
+    if ((time_t)tm.tm_year + 1900 != Year) {
+       errno = EOVERFLOW;
+       return -1;
+    }
     if (Timezone == USE_LOCAL_TIME) {
            switch (DSTmode) {
            case DSTon:  tm.tm_isdst = 1; break;
@@ -797,12 +884,29 @@
 {
     struct tm  tm;
     time_t     now;
+    time_t     change;
 
     now = Start;
     if (localtime_r(&now, &tm) == NULL)
        return -1;
-    now += SECSPERDAY * ((DayNumber - tm.tm_wday + 7) % 7);
-    now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
+
+    /* should be using TIME_T_MAX but there is no such thing, so just "know" */
+    if (llabs(DayOrdinal) >= LLONG_MAX / (7 * SECSPERDAY)) {
+       errno = EOVERFLOW;
+       return -1;
+    }
+
+    change = SECSPERDAY * ((DayNumber - tm.tm_wday + 7) % 7);
+    change += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
+
+    /* same here for _MAX and _MIN */
+    if ((change > 0 && LLONG_MAX - change < now) ||
+       (change < 0 && LLONG_MIN - change > now)) {
+           errno = EOVERFLOW;



Home | Main Index | Thread Index | Old Index