[Toybox] [PATCH] Reject invalid dates in date(1).

enh enh at google.com
Tue Jul 28 13:38:33 PDT 2015


(i haven't actually merged this in the Android tree, but my git-fu is
too weak to get a diff of a new file otherwise.)

Author: Elliott Hughes <enh at google.com>
Date:   Tue Jul 28 13:14:17 2015 -0700

    Reject invalid dates in date(1).

    Humans get upset when date(1) lets mktime(3) work out what the 99th day
    of the 99th month would be rather than rejecting the invalid date. For
    the subtly wrong cases, rather than get into the leap year business,
    let's rely on localtime_r(3).

    Bug: http://b/22788816

diff --git a/tests/date.test b/tests/date.test
new file mode 100755
index 0000000..e633809
--- /dev/null
+++ b/tests/date.test
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+[ -f testing.sh ] && . testing.sh
+
+#testing "name" "command" "result" "infile" "stdin"
+
+# Accidentally given a Unix time, we should trivially reject that.
+testing "date Unix time" "date 1438053157 2>&1" "date: bad date
'1438053157'\n" "" ""
+# But some invalid dates are more subtle, like Febuary 29th in a non-leap year.
+testing "date Feb 29th" "date 022900001975 2>&1" "date: bad date
'022900001975'\n" "" ""
diff --git a/toys/posix/date.c b/toys/posix/date.c
index d4c4524..54fe24e 100644
--- a/toys/posix/date.c
+++ b/toys/posix/date.c
@@ -51,6 +51,24 @@ GLOBALS(
   char *showdate;
 )

+// mktime(3) normalizes the struct tm fields, but date(1) shouldn't.
+// If we round trip via localtime_r(3) and get back where we started,
+// we know 'tm' is already in normal form.
+static time_t non_normalizing_mktime(struct tm *tm)
+{
+  struct tm tm0 = *tm;
+  time_t t = mktime(tm);
+  struct tm tm1;
+
+  if (t == -1 || !localtime_r(&t, &tm1) ||
+      tm0.tm_sec != tm1.tm_sec || tm0.tm_min != tm1.tm_min ||
+      tm0.tm_hour != tm1.tm_hour || tm0.tm_mday != tm1.tm_mday ||
+      tm0.tm_mon != tm1.tm_mon)
+    return -1;
+
+  return t;
+}
+
 // Handle default posix date format: mmddhhmm[[cc]yy]
 // returns 0 success, nonzero for error
 int parse_posixdate(char *str, struct tm *tm)
@@ -145,13 +163,13 @@ void date_main(void)
       // We can't just pass a timezone to mktime because posix.
       setenv("TZ", "UTC", 1);
       tzset();
-      tv.tv_sec = mktime(&tm);
+      tv.tv_sec = non_normalizing_mktime(&tm);
       if (CFG_TOYBOX_FREE) {
         if (tz) setenv("TZ", tz, 1);
         else unsetenv("TZ");
         tzset();
       }
-    } else tv.tv_sec = mktime(&tm);
+    } else tv.tv_sec = non_normalizing_mktime(&tm);
     if (tv.tv_sec == (time_t)-1) goto bad_date;

     tv.tv_usec = 0;


More information about the Toybox mailing list