Blob Blame History Raw
From fc286e2b3af5b2ed9aec44b520265bb0968f1660 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?P=C3=A1draig=20Brady?= <P@draigBrady.com>
Date: Mon, 24 Apr 2017 01:43:36 -0700
Subject: [PATCH 1/2] time_rz: fix heap buffer overflow vulnerability

This issue has been assigned CVE-2017-7476 and was
detected with American Fuzzy Lop 2.41b run on the
coreutils date(1) program with ASAN enabled.

  ERROR: AddressSanitizer: heap-buffer-overflow on address 0x...
  WRITE of size 8 at 0x60d00000cff8 thread T0
  #1 0x443020 in extend_abbrs lib/time_rz.c:88
  #2 0x443356 in save_abbr lib/time_rz.c:155
  #3 0x44393f in localtime_rz lib/time_rz.c:290
  #4 0x41e4fe in parse_datetime2 lib/parse-datetime.y:1798

A minimized reproducer is the following 120 byte TZ value,
which goes beyond the value of ABBR_SIZE_MIN (119) on x86_64.
Extend the aa...b portion to overwrite more of the heap.

  date -d $(printf 'TZ="aaa%020daaaaaab%089d"')

localtime_rz and mktime_z were affected since commit 4bc76593.
parse_datetime was affected since commit 4e6e16b3f.

* lib/time_rz.c (save_abbr): Rearrange the calculation determining
whether there is enough buffer space available.  The rearrangement
ensures we're only dealing with positive numbers, thus avoiding
the problematic promotion of signed to unsigned causing an invalid
comparison when zone_copy is more than ABBR_SIZE_MIN bytes beyond
the start of the buffer.
* tests/test-parse-datetime.c (main): Add a test case written by
Paul Eggert, which overwrites enough of the heap so that
standard glibc will fail with "free(): invalid pointer"
without the patch applied.
Reported and analyzed at https://bugzilla.redhat.com/1444774

Upstream-commit: 94e01571507835ff59dd8ce2a0b56a4b566965a4
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
 gnulib-tests/test-parse-datetime.c | 16 ++++++++++++++++
 lib/time_rz.c                      | 15 +++++++++++++--
 2 files changed, 29 insertions(+), 2 deletions(-)

diff --git a/gnulib-tests/test-parse-datetime.c b/gnulib-tests/test-parse-datetime.c
index b42a51c..b6fe457 100644
--- a/gnulib-tests/test-parse-datetime.c
+++ b/gnulib-tests/test-parse-datetime.c
@@ -432,5 +432,21 @@ main (int argc _GL_UNUSED, char **argv)
   ASSERT (   parse_datetime (&result, "TZ=\"\\\\\"", &now));
   ASSERT (   parse_datetime (&result, "TZ=\"\\\"\"", &now));
 
+  /* Outlandishly-long time zone abbreviations should not cause problems.  */
+  {
+    static char const bufprefix[] = "TZ=\"";
+    enum { tzname_len = 2000 };
+    static char const bufsuffix[] = "0\" 1970-01-01 01:02:03.123456789";
+    enum { bufsize = sizeof bufprefix - 1 + tzname_len + sizeof bufsuffix };
+    char buf[bufsize];
+    memcpy (buf, bufprefix, sizeof bufprefix - 1);
+    memset (buf + sizeof bufprefix - 1, 'X', tzname_len);
+    strcpy (buf + bufsize - sizeof bufsuffix, bufsuffix);
+    ASSERT (parse_datetime (&result, buf, &now));
+    LOG (buf, now, result);
+    ASSERT (result.tv_sec == 1 * 60 * 60 + 2 * 60 + 3
+            && result.tv_nsec == 123456789);
+  }
+
   return 0;
 }
diff --git a/lib/time_rz.c b/lib/time_rz.c
index adb9c1c..c41a8ef 100644
--- a/lib/time_rz.c
+++ b/lib/time_rz.c
@@ -27,6 +27,7 @@
 #include <time.h>
 
 #include <errno.h>
+#include <limits.h>
 #include <stdbool.h>
 #include <stddef.h>
 #include <stdlib.h>
@@ -35,6 +36,10 @@
 #include "flexmember.h"
 #include "time-internal.h"
 
+#ifndef SIZE_MAX
+# define SIZE_MAX ((size_t) -1)
+#endif
+
 #if !HAVE_TZSET
 static void tzset (void) { }
 #endif
@@ -43,7 +48,7 @@ static void tzset (void) { }
    the largest "small" request for the GNU C library malloc.  */
 enum { DEFAULT_MXFAST = 64 * sizeof (size_t) / 4 };
 
-/* Minimum size of the ABBRS member of struct abbr.  ABBRS is larger
+/* Minimum size of the ABBRS member of struct tm_zone.  ABBRS is larger
    only in the unlikely case where an abbreviation longer than this is
    used.  */
 enum { ABBR_SIZE_MIN = DEFAULT_MXFAST - offsetof (struct tm_zone, abbrs) };
@@ -150,7 +155,13 @@ save_abbr (timezone_t tz, struct tm *tm)
           if (! (*zone_copy || (zone_copy == tz->abbrs && tz->tz_is_set)))
             {
               size_t zone_size = strlen (zone) + 1;
-              if (zone_size < tz->abbrs + ABBR_SIZE_MIN - zone_copy)
+              size_t zone_used = zone_copy - tz->abbrs;
+              if (SIZE_MAX - zone_used < zone_size)
+                {
+                  errno = ENOMEM;
+                  return false;
+                }
+              if (zone_used + zone_size < ABBR_SIZE_MIN)
                 extend_abbrs (zone_copy, zone, zone_size);
               else
                 {
-- 
2.9.3


From 9579f90484c71e5a22f32f35189192a82e47550e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?P=C3=A1draig=20Brady?= <P@draigBrady.com>
Date: Wed, 26 Apr 2017 20:51:39 -0700
Subject: [PATCH 2/2] date,touch: test and document large TZ security issue

Add a test for CVE-2017-7476 which was fixed in gnulib at:
http://git.sv.gnu.org/gitweb/?p=gnulib.git;a=commitdiff;h=94e01571

* tests/misc/date-tz.sh: Add a new test which overwrites enough
of the heap to trigger a segfault, even without ASAN enabled.
* tests/local.mk: Reference the new test.
* NEWS: Mention the bug fix.

Upstream-commit: 9287ef2b1707e2a222f8ae776ce3785abcb16fba
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
 tests/local.mk        |  1 +
 tests/misc/date-tz.sh | 26 ++++++++++++++++++++++++++
 2 files changed, 27 insertions(+)
 create mode 100755 tests/misc/date-tz.sh

diff --git a/tests/local.mk b/tests/local.mk
index 9f1a853..ec0b414 100644
--- a/tests/local.mk
+++ b/tests/local.mk
@@ -282,6 +282,7 @@ all_tests =					\
   tests/misc/csplit-suppress-matched.pl		\
   tests/misc/date-debug.sh			\
   tests/misc/date-sec.sh			\
+  tests/misc/date-tz.sh				\
   tests/misc/dircolors.pl			\
   tests/misc/dirname.pl				\
   tests/misc/env-null.sh			\
diff --git a/tests/misc/date-tz.sh b/tests/misc/date-tz.sh
new file mode 100755
index 0000000..3fe1579
--- /dev/null
+++ b/tests/misc/date-tz.sh
@@ -0,0 +1,26 @@
+#!/bin/sh
+# Verify TZ processing.
+
+# Copyright (C) 2017 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ date
+
+# coreutils-8.27 would overwrite the heap with large TZ values
+tz_long=$(printf '%2000s' | tr ' ' a)
+date -d "TZ=\"${tz_long}0\" 2017" || fail=1
+
+Exit $fail
-- 
2.9.3