]> git.ipfire.org Git - thirdparty/glibc.git/commitdiff
Fix nan functions handling of payload strings (bug 16961, bug 16962).
authorJoseph Myers <joseph@codesourcery.com>
Fri, 4 Dec 2015 20:36:28 +0000 (20:36 +0000)
committerJoseph Myers <joseph@codesourcery.com>
Fri, 4 Dec 2015 20:36:28 +0000 (20:36 +0000)
The nan, nanf and nanl functions handle payload strings by doing e.g.:

  if (tagp[0] != '\0')
    {
      char buf[6 + strlen (tagp)];
      sprintf (buf, "NAN(%s)", tagp);
      return strtod (buf, NULL);
    }

This is an unbounded stack allocation based on the length of the
argument.  Furthermore, if the argument starts with an n-char-sequence
followed by ')', that n-char-sequence is wrongly treated as
significant for determining the payload of the resulting NaN, when ISO
C says the call should be equivalent to strtod ("NAN", NULL), without
being affected by that initial n-char-sequence.  This patch fixes both
those problems by using the __strtod_nan etc. functions recently
factored out of strtod etc. for that purpose, with those functions
being exported from libc at version GLIBC_PRIVATE.

Tested for x86_64, x86, mips64 and powerpc.

[BZ #16961]
[BZ #16962]
* math/s_nan.c (__nan): Use __strtod_nan instead of constructing a
string on the stack for strtod.
* math/s_nanf.c (__nanf): Use __strtof_nan instead of constructing
a string on the stack for strtof.
* math/s_nanl.c (__nanl): Use __strtold_nan instead of
constructing a string on the stack for strtold.
* stdlib/Versions (libc): Add __strtof_nan, __strtod_nan and
__strtold_nan to GLIBC_PRIVATE.
* math/test-nan-overflow.c: New file.
* math/test-nan-payload.c: Likewise.
* math/Makefile (tests): Add test-nan-overflow and
test-nan-payload.

ChangeLog
NEWS
math/Makefile
math/s_nan.c
math/s_nanf.c
math/s_nanl.c
math/test-nan-overflow.c [new file with mode: 0644]
math/test-nan-payload.c [new file with mode: 0644]
stdlib/Versions

index 266df03ef4e9cedf2f2177118e8de6b29c90a481..edd7ccf33a5fdbc1411480f96bb0589ea668e33d 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,20 @@
+2015-12-04  Joseph Myers  <joseph@codesourcery.com>
+
+       [BZ #16961]
+       [BZ #16962]
+       * math/s_nan.c (__nan): Use __strtod_nan instead of constructing a
+       string on the stack for strtod.
+       * math/s_nanf.c (__nanf): Use __strtof_nan instead of constructing
+       a string on the stack for strtof.
+       * math/s_nanl.c (__nanl): Use __strtold_nan instead of
+       constructing a string on the stack for strtold.
+       * stdlib/Versions (libc): Add __strtof_nan, __strtod_nan and
+       __strtold_nan to GLIBC_PRIVATE.
+       * math/test-nan-overflow.c: New file.
+       * math/test-nan-payload.c: Likewise.
+       * math/Makefile (tests): Add test-nan-overflow and
+       test-nan-payload.
+
 2015-12-04  Paul Eggert  <eggert@cs.ucla.edu>
 
        Consistency about byte vs character in string.texi
diff --git a/NEWS b/NEWS
index cb61a3a9f6a3393eb9c6f17d3bbbba99dcd4778c..37189f7947afff3300c9c594090b39a5b038d46c 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -60,6 +60,12 @@ Version 2.23
   C Library is GCC 4.7.  Older GCC versions, and non-GNU compilers, can
   still be used to compile programs using the GNU C Library.
 
+Security related changes:
+
+* The nan, nanf and nanl functions no longer have unbounded stack usage
+  depending on the length of the string passed as an argument to the
+  functions.  Reported by Joseph Myers.
+
 * The following bugs are resolved with this release:
 
   [The release manager will add the list generated by
index 20f90d984b9f37107e2a2a86f0c85ceb948cfb3d..32f5730f16c741f3a1250f12ff3efb9d219180b2 100644 (file)
@@ -113,7 +113,8 @@ tests = test-matherr test-fenv atest-exp atest-sincos atest-exp2 basic-test \
        test-signgam-finite-c99 test-signgam-finite-c11 \
        test-nearbyint-except-2 test-signgam-uchar test-signgam-uchar-init \
        test-signgam-uint test-signgam-uint-init test-signgam-ullong \
-       test-signgam-ullong-init $(tests-static)
+       test-signgam-ullong-init test-nan-overflow test-nan-payload \
+       $(tests-static)
 tests-static = test-fpucw-static test-fpucw-ieee-static \
               test-signgam-uchar-static test-signgam-uchar-init-static \
               test-signgam-uint-static test-signgam-uint-init-static \
index f4b30a26f58680f9003eb3072f42861857f66024..a6f3006ba48945b2874f6f9683dbf9e0015495d5 100644 (file)
 double
 __nan (const char *tagp)
 {
-  if (tagp[0] != '\0')
-    {
-      char buf[6 + strlen (tagp)];
-      sprintf (buf, "NAN(%s)", tagp);
-      return strtod (buf, NULL);
-    }
-
-  return NAN;
+  return __strtod_nan (tagp, NULL, 0);
 }
 weak_alias (__nan, nan)
 #ifdef NO_LONG_DOUBLE
index 2d3fcc580390f72e3f9206e3e30d261e920fb6cb..6a98422e6cef18e9635ae18dfea2e4cf2c3091c1 100644 (file)
 float
 __nanf (const char *tagp)
 {
-  if (tagp[0] != '\0')
-    {
-      char buf[6 + strlen (tagp)];
-      sprintf (buf, "NAN(%s)", tagp);
-      return strtof (buf, NULL);
-    }
-
-  return NAN;
+  return __strtof_nan (tagp, NULL, 0);
 }
 weak_alias (__nanf, nanf)
index 73387a91169a909a394bae35fff0b57ed83d597d..4e1fc806fd24e018556a5089ecb1ad2a197f528a 100644 (file)
 long double
 __nanl (const char *tagp)
 {
-  if (tagp[0] != '\0')
-    {
-      char buf[6 + strlen (tagp)];
-      sprintf (buf, "NAN(%s)", tagp);
-      return strtold (buf, NULL);
-    }
-
-  return NAN;
+  return __strtold_nan (tagp, NULL, 0);
 }
 weak_alias (__nanl, nanl)
diff --git a/math/test-nan-overflow.c b/math/test-nan-overflow.c
new file mode 100644 (file)
index 0000000..f56aaf3
--- /dev/null
@@ -0,0 +1,66 @@
+/* Test nan functions stack overflow (bug 16962).
+   Copyright (C) 2015 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 <math.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/resource.h>
+
+#define STACK_LIM 1048576
+#define STRING_SIZE (2 * STACK_LIM)
+
+static int
+do_test (void)
+{
+  int result = 0;
+  struct rlimit lim;
+  getrlimit (RLIMIT_STACK, &lim);
+  lim.rlim_cur = STACK_LIM;
+  setrlimit (RLIMIT_STACK, &lim);
+  char *nanstr = malloc (STRING_SIZE);
+  if (nanstr == NULL)
+    {
+      puts ("malloc failed, cannot test");
+      return 77;
+    }
+  memset (nanstr, '0', STRING_SIZE - 1);
+  nanstr[STRING_SIZE - 1] = 0;
+#define NAN_TEST(TYPE, FUNC)                   \
+  do                                           \
+    {                                          \
+      char *volatile p = nanstr;               \
+      volatile TYPE v = FUNC (p);              \
+      if (isnan (v))                           \
+       puts ("PASS: " #FUNC);                  \
+      else                                     \
+       {                                       \
+         puts ("FAIL: " #FUNC);                \
+         result = 1;                           \
+       }                                       \
+    }                                          \
+  while (0)
+  NAN_TEST (float, nanf);
+  NAN_TEST (double, nan);
+#ifndef NO_LONG_DOUBLE
+  NAN_TEST (long double, nanl);
+#endif
+  return result;
+}
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
diff --git a/math/test-nan-payload.c b/math/test-nan-payload.c
new file mode 100644 (file)
index 0000000..358ff71
--- /dev/null
@@ -0,0 +1,122 @@
+/* Test nan functions payload handling (bug 16961).
+   Copyright (C) 2015 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 <float.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Avoid built-in functions.  */
+#define WRAP_NAN(FUNC, STR) \
+  ({ const char *volatile wns = (STR); FUNC (wns); })
+#define WRAP_STRTO(FUNC, STR) \
+  ({ const char *volatile wss = (STR); FUNC (wss, NULL); })
+
+#define CHECK_IS_NAN(TYPE, A)                  \
+  do                                           \
+    {                                          \
+      if (isnan (A))                           \
+       puts ("PASS: " #TYPE " " #A);           \
+      else                                     \
+       {                                       \
+         puts ("FAIL: " #TYPE " " #A);         \
+         result = 1;                           \
+       }                                       \
+    }                                          \
+  while (0)
+
+#define CHECK_SAME_NAN(TYPE, A, B)                     \
+  do                                                   \
+    {                                                  \
+      if (memcmp (&(A), &(B), sizeof (A)) == 0)                \
+       puts ("PASS: " #TYPE " " #A " = " #B);          \
+      else                                             \
+       {                                               \
+         puts ("FAIL: " #TYPE " " #A " = " #B);        \
+         result = 1;                                   \
+       }                                               \
+    }                                                  \
+  while (0)
+
+#define CHECK_DIFF_NAN(TYPE, A, B)                     \
+  do                                                   \
+    {                                                  \
+      if (memcmp (&(A), &(B), sizeof (A)) != 0)                \
+       puts ("PASS: " #TYPE " " #A " != " #B);         \
+      else                                             \
+       {                                               \
+         puts ("FAIL: " #TYPE " " #A " != " #B);       \
+         result = 1;                                   \
+       }                                               \
+    }                                                  \
+  while (0)
+
+/* Cannot test payloads by memcmp for formats where NaNs have padding
+   bits.  */
+#define CAN_TEST_EQ(MANT_DIG) ((MANT_DIG) != 64 && (MANT_DIG) != 106)
+
+#define RUN_TESTS(TYPE, SFUNC, FUNC, MANT_DIG)         \
+  do                                                   \
+    {                                                  \
+     TYPE n123 = WRAP_NAN (FUNC, "123");               \
+     CHECK_IS_NAN (TYPE, n123);                                \
+     TYPE s123 = WRAP_STRTO (SFUNC, "NAN(123)");       \
+     CHECK_IS_NAN (TYPE, s123);                                \
+     TYPE n456 = WRAP_NAN (FUNC, "456");               \
+     CHECK_IS_NAN (TYPE, n456);                                \
+     TYPE s456 = WRAP_STRTO (SFUNC, "NAN(456)");       \
+     CHECK_IS_NAN (TYPE, s456);                                \
+     TYPE n123x = WRAP_NAN (FUNC, "123)");             \
+     CHECK_IS_NAN (TYPE, n123x);                       \
+     TYPE nemp = WRAP_NAN (FUNC, "");                  \
+     CHECK_IS_NAN (TYPE, nemp);                                \
+     TYPE semp = WRAP_STRTO (SFUNC, "NAN()");          \
+     CHECK_IS_NAN (TYPE, semp);                                \
+     TYPE sx = WRAP_STRTO (SFUNC, "NAN");              \
+     CHECK_IS_NAN (TYPE, sx);                          \
+     if (CAN_TEST_EQ (MANT_DIG))                       \
+       CHECK_SAME_NAN (TYPE, n123, s123);              \
+     if (CAN_TEST_EQ (MANT_DIG))                       \
+       CHECK_SAME_NAN (TYPE, n456, s456);              \
+     if (CAN_TEST_EQ (MANT_DIG))                       \
+       CHECK_SAME_NAN (TYPE, nemp, semp);              \
+     if (CAN_TEST_EQ (MANT_DIG))                       \
+       CHECK_SAME_NAN (TYPE, n123x, sx);               \
+     CHECK_DIFF_NAN (TYPE, n123, n456);                        \
+     CHECK_DIFF_NAN (TYPE, n123, nemp);                        \
+     CHECK_DIFF_NAN (TYPE, n123, n123x);               \
+     CHECK_DIFF_NAN (TYPE, n456, nemp);                        \
+     CHECK_DIFF_NAN (TYPE, n456, n123x);               \
+    }                                                  \
+  while (0)
+
+static int
+do_test (void)
+{
+  int result = 0;
+  RUN_TESTS (float, strtof, nanf, FLT_MANT_DIG);
+  RUN_TESTS (double, strtod, nan, DBL_MANT_DIG);
+#ifndef NO_LONG_DOUBLE
+  RUN_TESTS (long double, strtold, nanl, LDBL_MANT_DIG);
+#endif
+  return result;
+}
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
index f1777dfcf4d799a118d6a41d00538fdf0b951ae0..60b628d47a6215a8c8ca5622e6592ee283f05f1a 100644 (file)
@@ -118,5 +118,6 @@ libc {
     # Used from other libraries
     __libc_secure_getenv;
     __call_tls_dtors;
+    __strtof_nan; __strtod_nan; __strtold_nan;
   }
 }