[glib: 3/6] gtimer: Add overflow checks to g_time_val_from_iso8601()



commit cefa66eb760055ed00e9a45c7e91979c5bb1609a
Author: Philip Withnall <withnall endlessm com>
Date:   Fri Aug 3 14:20:29 2018 +0100

    gtimer: Add overflow checks to g_time_val_from_iso8601()
    
    The code was previously doing a few bits of arithmetic without checking
    whether they would overflow, and hence not validating the date/time it
    was parsing very strictly.
    
    The parser is now not 100% strict, but should avoid any arithmetic
    overflows which would cause an invalid result to be returned even though
    g_time_val_from_iso8601() had returned TRUE.
    
    oss-fuzz#9673
    
    Signed-off-by: Philip Withnall <withnall endlessm com>

 glib/gtimer.c      | 77 ++++++++++++++++++++++++++++++++++++++++++------------
 glib/tests/timer.c | 25 +++++++++++++++++-
 2 files changed, 84 insertions(+), 18 deletions(-)
---
diff --git a/glib/gtimer.c b/glib/gtimer.c
index db2f1b565..348b4befb 100644
--- a/glib/gtimer.c
+++ b/glib/gtimer.c
@@ -357,6 +357,8 @@ g_time_val_from_iso8601 (const gchar *iso_date,
 {
   struct tm tm = {0};
   long val;
+  long mday, mon, year;
+  long hour, min, sec;
 
   g_return_val_if_fail (iso_date != NULL, FALSE);
   g_return_val_if_fail (time_ != NULL, FALSE);
@@ -377,23 +379,35 @@ g_time_val_from_iso8601 (const gchar *iso_date,
   if (*iso_date == '-')
     {
       /* YYYY-MM-DD */
-      tm.tm_year = val - 1900;
+      year = val;
       iso_date++;
-      tm.tm_mon = strtoul (iso_date, (char **)&iso_date, 10) - 1;
-      
+
+      mon = strtoul (iso_date, (char **)&iso_date, 10);
       if (*iso_date++ != '-')
         return FALSE;
       
-      tm.tm_mday = strtoul (iso_date, (char **)&iso_date, 10);
+      mday = strtoul (iso_date, (char **)&iso_date, 10);
     }
   else
     {
       /* YYYYMMDD */
-      tm.tm_mday = val % 100;
-      tm.tm_mon = (val % 10000) / 100 - 1;
-      tm.tm_year = val / 10000 - 1900;
+      mday = val % 100;
+      mon = (val % 10000) / 100;
+      year = val / 10000;
     }
 
+  /* Validation. */
+  if (year < 1900 || year > G_MAXINT)
+    return FALSE;
+  if (mon < 1 || mon > 12)
+    return FALSE;
+  if (mday < 1 || mday > 31)
+    return FALSE;
+
+  tm.tm_mday = mday;
+  tm.tm_mon = mon - 1;
+  tm.tm_year = year - 1900;
+
   if (*iso_date != 'T')
     return FALSE;
 
@@ -407,34 +421,50 @@ g_time_val_from_iso8601 (const gchar *iso_date,
   if (*iso_date == ':')
     {
       /* hh:mm:ss */
-      tm.tm_hour = val;
+      hour = val;
       iso_date++;
-      tm.tm_min = strtoul (iso_date, (char **)&iso_date, 10);
+      min = strtoul (iso_date, (char **)&iso_date, 10);
       
       if (*iso_date++ != ':')
         return FALSE;
       
-      tm.tm_sec = strtoul (iso_date, (char **)&iso_date, 10);
+      sec = strtoul (iso_date, (char **)&iso_date, 10);
     }
   else
     {
       /* hhmmss */
-      tm.tm_sec = val % 100;
-      tm.tm_min = (val % 10000) / 100;
-      tm.tm_hour = val / 10000;
+      sec = val % 100;
+      min = (val % 10000) / 100;
+      hour = val / 10000;
     }
 
+  /* Validation. Allow up to 2 leap seconds when validating @sec. */
+  if (hour > 23)
+    return FALSE;
+  if (min > 59)
+    return FALSE;
+  if (sec > 61)
+    return FALSE;
+
+  tm.tm_hour = hour;
+  tm.tm_min = min;
+  tm.tm_sec = sec;
+
   time_->tv_usec = 0;
   
   if (*iso_date == ',' || *iso_date == '.')
     {
       glong mul = 100000;
 
-      while (g_ascii_isdigit (*++iso_date))
+      while (mul >= 1 && g_ascii_isdigit (*++iso_date))
         {
           time_->tv_usec += (*iso_date - '0') * mul;
           mul /= 10;
         }
+
+      /* Skip any remaining digits after we’ve reached our limit of precision. */
+      while (g_ascii_isdigit (*iso_date))
+        iso_date++;
     }
     
   /* Now parse the offset and convert tm to a time_t */
@@ -450,11 +480,24 @@ g_time_val_from_iso8601 (const gchar *iso_date,
       val = strtoul (iso_date + 1, (char **)&iso_date, 10);
       
       if (*iso_date == ':')
-        val = 60 * val + strtoul (iso_date + 1, (char **)&iso_date, 10);
+        {
+          /* hh:mm */
+          hour = val;
+          min = strtoul (iso_date + 1, (char **)&iso_date, 10);
+        }
       else
-        val = 60 * (val / 100) + (val % 100);
+        {
+          /* hhmm */
+          hour = val / 100;
+          min = val % 100;
+        }
+
+      if (hour > 99)
+        return FALSE;
+      if (min > 59)
+        return FALSE;
 
-      time_->tv_sec = mktime_utc (&tm) + (time_t) (60 * val * sign);
+      time_->tv_sec = mktime_utc (&tm) + (time_t) (60 * (60 * hour + min) * sign);
     }
   else
     {
diff --git a/glib/tests/timer.c b/glib/tests/timer.c
index cb9a2686c..dc08d4c7c 100644
--- a/glib/tests/timer.c
+++ b/glib/tests/timer.c
@@ -144,7 +144,30 @@ test_timeval_from_iso8601 (void)
     { FALSE, "2001-10-08Tx", { 0, 0 } },
     { FALSE, "2001-10-08T10:11x", { 0, 0 } },
     { FALSE, "Wed Dec 19 17:20:20 GMT 2007", { 0, 0 } },
-    { FALSE, "1980-02-22T10:36:00Zulu", { 0, 0 } }
+    { FALSE, "1980-02-22T10:36:00Zulu", { 0, 0 } },
+    { FALSE, "2T0+819855292164632335", { 0, 0 } },
+    { TRUE, "2018-08-03T14:08:05.446178377+01:00", { 1533301685, 446178 } },
+    { FALSE, "2147483648-08-03T14:08:05.446178377+01:00", { 0, 0 } },
+    { FALSE, "2018-13-03T14:08:05.446178377+01:00", { 0, 0 } },
+    { FALSE, "2018-00-03T14:08:05.446178377+01:00", { 0, 0 } },
+    { FALSE, "2018-08-00T14:08:05.446178377+01:00", { 0, 0 } },
+    { FALSE, "2018-08-32T14:08:05.446178377+01:00", { 0, 0 } },
+    { FALSE, "2018-08-03T24:08:05.446178377+01:00", { 0, 0 } },
+    { FALSE, "2018-08-03T14:60:05.446178377+01:00", { 0, 0 } },
+    { FALSE, "2018-08-03T14:08:63.446178377+01:00", { 0, 0 } },
+    { FALSE, "2018-08-03T14:08:05.446178377+100:00", { 0, 0 } },
+    { FALSE, "2018-08-03T14:08:05.446178377+01:60", { 0, 0 } },
+    { TRUE, "20180803T140805.446178377+0100", { 1533301685, 446178 } },
+    { FALSE, "21474836480803T140805.446178377+0100", { 0, 0 } },
+    { FALSE, "20181303T140805.446178377+0100", { 0, 0 } },
+    { FALSE, "20180003T140805.446178377+0100", { 0, 0 } },
+    { FALSE, "20180800T140805.446178377+0100", { 0, 0 } },
+    { FALSE, "20180832T140805.446178377+0100", { 0, 0 } },
+    { FALSE, "20180803T240805.446178377+0100", { 0, 0 } },
+    { FALSE, "20180803T146005.446178377+0100", { 0, 0 } },
+    { FALSE, "20180803T140863.446178377+0100", { 0, 0 } },
+    { FALSE, "20180803T140805.446178377+10000", { 0, 0 } },
+    { FALSE, "20180803T140805.446178377+0160", { 0, 0 } },
   };
   GTimeVal out;
   gboolean success;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]