Kamil Dudka e0567d
From fc286e2b3af5b2ed9aec44b520265bb0968f1660 Mon Sep 17 00:00:00 2001
Kamil Dudka e0567d
From: =?UTF-8?q?P=C3=A1draig=20Brady?= <P@draigBrady.com>
Kamil Dudka e0567d
Date: Mon, 24 Apr 2017 01:43:36 -0700
Kamil Dudka e0567d
Subject: [PATCH 1/2] time_rz: fix heap buffer overflow vulnerability
Kamil Dudka e0567d
Kamil Dudka e0567d
This issue has been assigned CVE-2017-7476 and was
Kamil Dudka e0567d
detected with American Fuzzy Lop 2.41b run on the
Kamil Dudka e0567d
coreutils date(1) program with ASAN enabled.
Kamil Dudka e0567d
Kamil Dudka e0567d
  ERROR: AddressSanitizer: heap-buffer-overflow on address 0x...
Kamil Dudka e0567d
  WRITE of size 8 at 0x60d00000cff8 thread T0
Kamil Dudka e0567d
  #1 0x443020 in extend_abbrs lib/time_rz.c:88
Kamil Dudka e0567d
  #2 0x443356 in save_abbr lib/time_rz.c:155
Kamil Dudka e0567d
  #3 0x44393f in localtime_rz lib/time_rz.c:290
Kamil Dudka e0567d
  #4 0x41e4fe in parse_datetime2 lib/parse-datetime.y:1798
Kamil Dudka e0567d
Kamil Dudka e0567d
A minimized reproducer is the following 120 byte TZ value,
Kamil Dudka e0567d
which goes beyond the value of ABBR_SIZE_MIN (119) on x86_64.
Kamil Dudka e0567d
Extend the aa...b portion to overwrite more of the heap.
Kamil Dudka e0567d
Kamil Dudka e0567d
  date -d $(printf 'TZ="aaa%020daaaaaab%089d"')
Kamil Dudka e0567d
Kamil Dudka e0567d
localtime_rz and mktime_z were affected since commit 4bc76593.
Kamil Dudka e0567d
parse_datetime was affected since commit 4e6e16b3f.
Kamil Dudka e0567d
Kamil Dudka e0567d
* lib/time_rz.c (save_abbr): Rearrange the calculation determining
Kamil Dudka e0567d
whether there is enough buffer space available.  The rearrangement
Kamil Dudka e0567d
ensures we're only dealing with positive numbers, thus avoiding
Kamil Dudka e0567d
the problematic promotion of signed to unsigned causing an invalid
Kamil Dudka e0567d
comparison when zone_copy is more than ABBR_SIZE_MIN bytes beyond
Kamil Dudka e0567d
the start of the buffer.
Kamil Dudka e0567d
* tests/test-parse-datetime.c (main): Add a test case written by
Kamil Dudka e0567d
Paul Eggert, which overwrites enough of the heap so that
Kamil Dudka e0567d
standard glibc will fail with "free(): invalid pointer"
Kamil Dudka e0567d
without the patch applied.
Kamil Dudka e0567d
Reported and analyzed at https://bugzilla.redhat.com/1444774
Kamil Dudka e0567d
Kamil Dudka e0567d
Upstream-commit: 94e01571507835ff59dd8ce2a0b56a4b566965a4
Kamil Dudka e0567d
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
Kamil Dudka e0567d
---
Kamil Dudka e0567d
 gnulib-tests/test-parse-datetime.c | 16 ++++++++++++++++
Kamil Dudka e0567d
 lib/time_rz.c                      | 15 +++++++++++++--
Kamil Dudka e0567d
 2 files changed, 29 insertions(+), 2 deletions(-)
Kamil Dudka e0567d
Kamil Dudka e0567d
diff --git a/gnulib-tests/test-parse-datetime.c b/gnulib-tests/test-parse-datetime.c
Kamil Dudka e0567d
index b42a51c..b6fe457 100644
Kamil Dudka e0567d
--- a/gnulib-tests/test-parse-datetime.c
Kamil Dudka e0567d
+++ b/gnulib-tests/test-parse-datetime.c
Kamil Dudka e0567d
@@ -432,5 +432,21 @@ main (int argc _GL_UNUSED, char **argv)
Kamil Dudka e0567d
   ASSERT (   parse_datetime (&result, "TZ=\"\\\\\"", &now));
Kamil Dudka e0567d
   ASSERT (   parse_datetime (&result, "TZ=\"\\\"\"", &now));
Kamil Dudka e0567d
 
Kamil Dudka e0567d
+  /* Outlandishly-long time zone abbreviations should not cause problems.  */
Kamil Dudka e0567d
+  {
Kamil Dudka e0567d
+    static char const bufprefix[] = "TZ=\"";
Kamil Dudka e0567d
+    enum { tzname_len = 2000 };
Kamil Dudka e0567d
+    static char const bufsuffix[] = "0\" 1970-01-01 01:02:03.123456789";
Kamil Dudka e0567d
+    enum { bufsize = sizeof bufprefix - 1 + tzname_len + sizeof bufsuffix };
Kamil Dudka e0567d
+    char buf[bufsize];
Kamil Dudka e0567d
+    memcpy (buf, bufprefix, sizeof bufprefix - 1);
Kamil Dudka e0567d
+    memset (buf + sizeof bufprefix - 1, 'X', tzname_len);
Kamil Dudka e0567d
+    strcpy (buf + bufsize - sizeof bufsuffix, bufsuffix);
Kamil Dudka e0567d
+    ASSERT (parse_datetime (&result, buf, &now));
Kamil Dudka e0567d
+    LOG (buf, now, result);
Kamil Dudka e0567d
+    ASSERT (result.tv_sec == 1 * 60 * 60 + 2 * 60 + 3
Kamil Dudka e0567d
+            && result.tv_nsec == 123456789);
Kamil Dudka e0567d
+  }
Kamil Dudka e0567d
+
Kamil Dudka e0567d
   return 0;
Kamil Dudka e0567d
 }
Kamil Dudka e0567d
diff --git a/lib/time_rz.c b/lib/time_rz.c
Kamil Dudka e0567d
index adb9c1c..c41a8ef 100644
Kamil Dudka e0567d
--- a/lib/time_rz.c
Kamil Dudka e0567d
+++ b/lib/time_rz.c
Kamil Dudka e0567d
@@ -27,6 +27,7 @@
Kamil Dudka e0567d
 #include <time.h>
Kamil Dudka e0567d
 
Kamil Dudka e0567d
 #include <errno.h>
Kamil Dudka e0567d
+#include <limits.h>
Kamil Dudka e0567d
 #include <stdbool.h>
Kamil Dudka e0567d
 #include <stddef.h>
Kamil Dudka e0567d
 #include <stdlib.h>
Kamil Dudka e0567d
@@ -35,6 +36,10 @@
Kamil Dudka e0567d
 #include "flexmember.h"
Kamil Dudka e0567d
 #include "time-internal.h"
Kamil Dudka e0567d
 
Kamil Dudka e0567d
+#ifndef SIZE_MAX
Kamil Dudka e0567d
+# define SIZE_MAX ((size_t) -1)
Kamil Dudka e0567d
+#endif
Kamil Dudka e0567d
+
Kamil Dudka e0567d
 #if !HAVE_TZSET
Kamil Dudka e0567d
 static void tzset (void) { }
Kamil Dudka e0567d
 #endif
Kamil Dudka e0567d
@@ -43,7 +48,7 @@ static void tzset (void) { }
Kamil Dudka e0567d
    the largest "small" request for the GNU C library malloc.  */
Kamil Dudka e0567d
 enum { DEFAULT_MXFAST = 64 * sizeof (size_t) / 4 };
Kamil Dudka e0567d
 
Kamil Dudka e0567d
-/* Minimum size of the ABBRS member of struct abbr.  ABBRS is larger
Kamil Dudka e0567d
+/* Minimum size of the ABBRS member of struct tm_zone.  ABBRS is larger
Kamil Dudka e0567d
    only in the unlikely case where an abbreviation longer than this is
Kamil Dudka e0567d
    used.  */
Kamil Dudka e0567d
 enum { ABBR_SIZE_MIN = DEFAULT_MXFAST - offsetof (struct tm_zone, abbrs) };
Kamil Dudka e0567d
@@ -150,7 +155,13 @@ save_abbr (timezone_t tz, struct tm *tm)
Kamil Dudka e0567d
           if (! (*zone_copy || (zone_copy == tz->abbrs && tz->tz_is_set)))
Kamil Dudka e0567d
             {
Kamil Dudka e0567d
               size_t zone_size = strlen (zone) + 1;
Kamil Dudka e0567d
-              if (zone_size < tz->abbrs + ABBR_SIZE_MIN - zone_copy)
Kamil Dudka e0567d
+              size_t zone_used = zone_copy - tz->abbrs;
Kamil Dudka e0567d
+              if (SIZE_MAX - zone_used < zone_size)
Kamil Dudka e0567d
+                {
Kamil Dudka e0567d
+                  errno = ENOMEM;
Kamil Dudka e0567d
+                  return false;
Kamil Dudka e0567d
+                }
Kamil Dudka e0567d
+              if (zone_used + zone_size < ABBR_SIZE_MIN)
Kamil Dudka e0567d
                 extend_abbrs (zone_copy, zone, zone_size);
Kamil Dudka e0567d
               else
Kamil Dudka e0567d
                 {
Kamil Dudka e0567d
-- 
Kamil Dudka e0567d
2.9.3
Kamil Dudka e0567d
Kamil Dudka e0567d
Kamil Dudka e0567d
From 9579f90484c71e5a22f32f35189192a82e47550e Mon Sep 17 00:00:00 2001
Kamil Dudka e0567d
From: =?UTF-8?q?P=C3=A1draig=20Brady?= <P@draigBrady.com>
Kamil Dudka e0567d
Date: Wed, 26 Apr 2017 20:51:39 -0700
Kamil Dudka e0567d
Subject: [PATCH 2/2] date,touch: test and document large TZ security issue
Kamil Dudka e0567d
Kamil Dudka e0567d
Add a test for CVE-2017-7476 which was fixed in gnulib at:
Kamil Dudka e0567d
http://git.sv.gnu.org/gitweb/?p=gnulib.git;a=commitdiff;h=94e01571
Kamil Dudka e0567d
Kamil Dudka e0567d
* tests/misc/date-tz.sh: Add a new test which overwrites enough
Kamil Dudka e0567d
of the heap to trigger a segfault, even without ASAN enabled.
Kamil Dudka e0567d
* tests/local.mk: Reference the new test.
Kamil Dudka e0567d
* NEWS: Mention the bug fix.
Kamil Dudka e0567d
Kamil Dudka e0567d
Upstream-commit: 9287ef2b1707e2a222f8ae776ce3785abcb16fba
Kamil Dudka e0567d
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
Kamil Dudka e0567d
---
Kamil Dudka e0567d
 tests/local.mk        |  1 +
Kamil Dudka e0567d
 tests/misc/date-tz.sh | 26 ++++++++++++++++++++++++++
Kamil Dudka e0567d
 2 files changed, 27 insertions(+)
Kamil Dudka e0567d
 create mode 100755 tests/misc/date-tz.sh
Kamil Dudka e0567d
Kamil Dudka e0567d
diff --git a/tests/local.mk b/tests/local.mk
Kamil Dudka e0567d
index 9f1a853..ec0b414 100644
Kamil Dudka e0567d
--- a/tests/local.mk
Kamil Dudka e0567d
+++ b/tests/local.mk
Kamil Dudka e0567d
@@ -282,6 +282,7 @@ all_tests =					\
Kamil Dudka e0567d
   tests/misc/csplit-suppress-matched.pl		\
Kamil Dudka e0567d
   tests/misc/date-debug.sh			\
Kamil Dudka e0567d
   tests/misc/date-sec.sh			\
Kamil Dudka e0567d
+  tests/misc/date-tz.sh				\
Kamil Dudka e0567d
   tests/misc/dircolors.pl			\
Kamil Dudka e0567d
   tests/misc/dirname.pl				\
Kamil Dudka e0567d
   tests/misc/env-null.sh			\
Kamil Dudka e0567d
diff --git a/tests/misc/date-tz.sh b/tests/misc/date-tz.sh
Kamil Dudka e0567d
new file mode 100755
Kamil Dudka e0567d
index 0000000..3fe1579
Kamil Dudka e0567d
--- /dev/null
Kamil Dudka e0567d
+++ b/tests/misc/date-tz.sh
Kamil Dudka e0567d
@@ -0,0 +1,26 @@
Kamil Dudka e0567d
+#!/bin/sh
Kamil Dudka e0567d
+# Verify TZ processing.
Kamil Dudka e0567d
+
Kamil Dudka e0567d
+# Copyright (C) 2017 Free Software Foundation, Inc.
Kamil Dudka e0567d
+
Kamil Dudka e0567d
+# This program is free software: you can redistribute it and/or modify
Kamil Dudka e0567d
+# it under the terms of the GNU General Public License as published by
Kamil Dudka e0567d
+# the Free Software Foundation, either version 3 of the License, or
Kamil Dudka e0567d
+# (at your option) any later version.
Kamil Dudka e0567d
+
Kamil Dudka e0567d
+# This program is distributed in the hope that it will be useful,
Kamil Dudka e0567d
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
Kamil Dudka e0567d
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Kamil Dudka e0567d
+# GNU General Public License for more details.
Kamil Dudka e0567d
+
Kamil Dudka e0567d
+# You should have received a copy of the GNU General Public License
Kamil Dudka e0567d
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
Kamil Dudka e0567d
+
Kamil Dudka e0567d
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
Kamil Dudka e0567d
+print_ver_ date
Kamil Dudka e0567d
+
Kamil Dudka e0567d
+# coreutils-8.27 would overwrite the heap with large TZ values
Kamil Dudka e0567d
+tz_long=$(printf '%2000s' | tr ' ' a)
Kamil Dudka e0567d
+date -d "TZ=\"${tz_long}0\" 2017" || fail=1
Kamil Dudka e0567d
+
Kamil Dudka e0567d
+Exit $fail
Kamil Dudka e0567d
-- 
Kamil Dudka e0567d
2.9.3
Kamil Dudka e0567d