about summary refs log tree commit diff
path: root/resolv
diff options
context:
space:
mode:
Diffstat (limited to 'resolv')
-rw-r--r--resolv/Makefile2
-rw-r--r--resolv/res_debug.c24
-rw-r--r--resolv/tst-p_secstodate.c67
3 files changed, 92 insertions, 1 deletions
diff --git a/resolv/Makefile b/resolv/Makefile
index 03234964c8..aff671042e 100644
--- a/resolv/Makefile
+++ b/resolv/Makefile
@@ -55,6 +55,7 @@ tests += \
   tst-resolv-network \
   tst-resolv-res_init-multi \
   tst-resolv-search \
+  tst-p_secstodate \
 
 # These tests need libdl.
 ifeq (yes,$(build-shared))
@@ -176,6 +177,7 @@ $(objpfx)tst-ns_name.out: tst-ns_name.data
 $(objpfx)tst-ns_name_compress: $(objpfx)libresolv.so
 $(objpfx)tst-ns_name_pton: $(objpfx)libresolv.so
 $(objpfx)tst-res_hnok: $(objpfx)libresolv.so
+$(objpfx)tst-p_secstodate: $(objpfx)libresolv.so
 
 
 # This test case uses the deprecated RES_USE_INET6 resolver option.
diff --git a/resolv/res_debug.c b/resolv/res_debug.c
index 4114c2db46..a4fbc569b6 100644
--- a/resolv/res_debug.c
+++ b/resolv/res_debug.c
@@ -107,6 +107,7 @@
 #include <string.h>
 #include <time.h>
 #include <shlib-compat.h>
+#include <libc-diag.h>
 
 #ifdef SPRINTF_CHAR
 # define SPRINTF(x) strlen(sprintf/**/x)
@@ -1054,6 +1055,8 @@ libresolv_hidden_def (__dn_count_labels)
 /*
  * Make dates expressed in seconds-since-Jan-1-1970 easy to read.
  * SIG records are required to be printed like this, by the Secure DNS RFC.
+ * This is an obsolescent function and does not handle dates outside the
+ * signed 32-bit range.
  */
 char *
 p_secstodate (u_long secs) {
@@ -1063,12 +1066,31 @@ p_secstodate (u_long secs) {
 	struct tm *time;
 
 	struct tm timebuf;
-	time = __gmtime_r(&clock, &timebuf);
+	/* The call to __gmtime_r can never produce a year overflowing
+	   the range of int, given the check on SECS, but check for a
+	   NULL return anyway to avoid a null pointer dereference in
+	   case there are any other unspecified errors.  */
+	if (secs > 0x7fffffff
+	    || (time = __gmtime_r (&clock, &timebuf)) == NULL) {
+		strcpy (output, "<overflow>");
+		__set_errno (EOVERFLOW);
+		return output;
+	}
 	time->tm_year += 1900;
 	time->tm_mon += 1;
+	/* The struct tm fields, given the above range check,
+	   must have values that mean this sprintf exactly fills the
+	   buffer.  But as of GCC 8 of 2017-11-21, GCC cannot tell
+	   that, even given range checks on all fields with
+	   __builtin_unreachable called for out-of-range values.  */
+	DIAG_PUSH_NEEDS_COMMENT;
+#if __GNUC_PREREQ (7, 0)
+	DIAG_IGNORE_NEEDS_COMMENT (8, "-Wformat-overflow=");
+#endif
 	sprintf(output, "%04d%02d%02d%02d%02d%02d",
 		time->tm_year, time->tm_mon, time->tm_mday,
 		time->tm_hour, time->tm_min, time->tm_sec);
+	DIAG_POP_NEEDS_COMMENT;
 	return (output);
 }
 libresolv_hidden_def (__p_secstodate)
diff --git a/resolv/tst-p_secstodate.c b/resolv/tst-p_secstodate.c
new file mode 100644
index 0000000000..9dac1ad819
--- /dev/null
+++ b/resolv/tst-p_secstodate.c
@@ -0,0 +1,67 @@
+/* Test p_secstodate.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <array_length.h>
+#include <limits.h>
+#include <resolv.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+struct test
+{
+  /* Argument to p_secstodate.  */
+  unsigned long int in;
+  /* Expected output.  */
+  const char *out;
+};
+
+static const struct test tests[] =
+  {
+    { 0UL, "19700101000000" },
+    { 12345UL, "19700101032545" },
+    { 999999999UL, "20010909014639" },
+    { 2147483647UL, "20380119031407" },
+    { 2147483648UL, "<overflow>" },
+    { 4294967295UL, "<overflow>" },
+#if ULONG_MAX > 0xffffffffUL
+    { 4294967296UL, "<overflow>" },
+    { 9999999999UL, "<overflow>" },
+    { LONG_MAX, "<overflow>" },
+    { ULONG_MAX, "<overflow>" },
+#endif
+  };
+
+static int
+do_test (void)
+{
+  int ret = 0;
+  for (size_t i = 0; i < array_length (tests); i++)
+    {
+      char *p = p_secstodate (tests[i].in);
+      printf ("Test %zu: %lu -> %s\n", i, tests[i].in, p);
+      if (strcmp (p, tests[i].out) != 0)
+	{
+	  printf ("test %zu failed", i);
+	  ret = 1;
+	}
+    }
+  return ret;
+}
+
+#include <support/test-driver.c>