diff --git a/ChangeLog b/ChangeLog index 203141b15c..ba239a47a2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,17 @@ 2017-11-22 Joseph Myers + [BZ #22463] + * resolv/res_debug.c: Include . + (p_secstodate): Assert time_t at least as wide as u_long. On + overflow, use integer seconds since the epoch as output, or use + "" as output and set errno to EOVERFLOW if integer + seconds since the epoch would be 14 or more characters. + (p_secstodate) [__GNUC_PREREQ (7, 0)]: Disable -Wformat-overflow= + for sprintf call. + * resolv/tst-p_secstodate.c: New file. + * resolv/Makefile (tests): Add tst-p_secstodate. + ($(objpfx)tst-p_secstodate): Depend on $(objpfx)libresolv.so. + * sysdeps/sparc/sparc64/soft-fp/s_frexpl.c: Remove file. * sysdeps/sparc/sparc64/soft-fp/s_scalblnl.c: Likewise. * sysdeps/sparc/sparc64/soft-fp/s_scalbnl.c: Likewise. 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 #include #include +#include #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, ""); + __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 + . */ + +#include +#include +#include +#include +#include +#include + +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, "" }, + { 4294967295UL, "" }, +#if ULONG_MAX > 0xffffffffUL + { 4294967296UL, "" }, + { 9999999999UL, "" }, + { LONG_MAX, "" }, + { ULONG_MAX, "" }, +#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