Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Refactor strtod parsing of NaN payloads.
The nan* functions handle their string argument by constructing a
NAN(...) string on the stack as a VLA and passing it to strtod
functions.

This approach has problems discussed in bug 16961 and bug 16962: the
stack usage is unbounded, and it gives incorrect results in certain
cases where the argument is not a valid n-char-sequence.

The natural fix for both issues is to refactor the NaN payload parsing
out of strtod into a separate function that the nan* functions can
call directly, so that no temporary string needs constructing on the
stack at all.  This patch does that refactoring in preparation for
fixing those bugs (but without actually using the new functions from
nan* - which will also require exporting them from libc at version
GLIBC_PRIVATE).  This patch is not intended to change any user-visible
behavior, so no tests are added (fixes for the above bugs will of
course add tests for them).

This patch builds on my recent fixes for strtol and strtod issues in
Turkish locales.  Given those fixes, the parsing of NaN payloads is
locale-independent; thus, the new functions do not need to take a
locale_t argument.

Tested for x86_64, x86, mips64 and powerpc.

	* stdlib/strtod_nan.c: New file.
	* stdlib/strtod_nan_double.h: Likewise.
	* stdlib/strtod_nan_float.h: Likewise.
	* stdlib/strtod_nan_main.c: Likewise.
	* stdlib/strtod_nan_narrow.h: Likewise.
	* stdlib/strtod_nan_wide.h: Likewise.
	* stdlib/strtof_nan.c: Likewise.
	* stdlib/strtold_nan.c: Likewise.
	* sysdeps/ieee754/ldbl-128/strtod_nan_ldouble.h: Likewise.
	* sysdeps/ieee754/ldbl-128ibm/strtod_nan_ldouble.h: Likewise.
	* sysdeps/ieee754/ldbl-96/strtod_nan_ldouble.h: Likewise.
	* wcsmbs/wcstod_nan.c: Likewise.
	* wcsmbs/wcstof_nan.c: Likewise.
	* wcsmbs/wcstold_nan.c: Likewise.
	* stdlib/Makefile (routines): Add strtof_nan, strtod_nan and
	strtold_nan.
	* wcsmbs/Makefile (routines): Add wcstod_nan, wcstold_nan and
	wcstof_nan.
	* include/stdlib.h (__strtof_nan): Declare and use
	libc_hidden_proto.
	(__strtod_nan): Likewise.
	(__strtold_nan): Likewise.
	(__wcstof_nan): Likewise.
	(__wcstod_nan): Likewise.
	(__wcstold_nan): Likewise.
	* include/wchar.h (____wcstoull_l_internal): Declare.
	* stdlib/strtod_l.c: Do not include <ieee754.h>.
	(____strtoull_l_internal): Remove declaration.
	(STRTOF_NAN): Define macro.
	(SET_MANTISSA): Remove macro.
	(STRTOULL): Likewise.
	(____STRTOF_INTERNAL): Use STRTOF_NAN to parse NaN payload.
	* stdlib/strtof_l.c (____strtoull_l_internal): Remove declaration.
	(STRTOF_NAN): Define macro.
	(SET_MANTISSA): Remove macro.
	* sysdeps/ieee754/ldbl-128/strtold_l.c (STRTOF_NAN): Define macro.
	(SET_MANTISSA): Remove macro.
	* sysdeps/ieee754/ldbl-128ibm/strtold_l.c (STRTOF_NAN): Define
	macro.
	(SET_MANTISSA): Remove macro.
	* sysdeps/ieee754/ldbl-64-128/strtold_l.c (STRTOF_NAN): Define
	macro.
	(SET_MANTISSA): Remove macro.
	* sysdeps/ieee754/ldbl-96/strtold_l.c (STRTOF_NAN): Define macro.
	(SET_MANTISSA): Remove macro.
	* wcsmbs/wcstod_l.c (____wcstoull_l_internal): Remove declaration.
	* wcsmbs/wcstof_l.c (____wcstoull_l_internal): Likewise.
	* wcsmbs/wcstold_l.c (____wcstoull_l_internal): Likewise.
  • Loading branch information
Joseph Myers committed Nov 24, 2015
1 parent a7f0c5a commit e02cabe
Show file tree
Hide file tree
Showing 28 changed files with 504 additions and 95 deletions.
49 changes: 49 additions & 0 deletions ChangeLog
@@ -1,5 +1,54 @@
2015-11-24 Joseph Myers <joseph@codesourcery.com>

* stdlib/strtod_nan.c: New file.
* stdlib/strtod_nan_double.h: Likewise.
* stdlib/strtod_nan_float.h: Likewise.
* stdlib/strtod_nan_main.c: Likewise.
* stdlib/strtod_nan_narrow.h: Likewise.
* stdlib/strtod_nan_wide.h: Likewise.
* stdlib/strtof_nan.c: Likewise.
* stdlib/strtold_nan.c: Likewise.
* sysdeps/ieee754/ldbl-128/strtod_nan_ldouble.h: Likewise.
* sysdeps/ieee754/ldbl-128ibm/strtod_nan_ldouble.h: Likewise.
* sysdeps/ieee754/ldbl-96/strtod_nan_ldouble.h: Likewise.
* wcsmbs/wcstod_nan.c: Likewise.
* wcsmbs/wcstof_nan.c: Likewise.
* wcsmbs/wcstold_nan.c: Likewise.
* stdlib/Makefile (routines): Add strtof_nan, strtod_nan and
strtold_nan.
* wcsmbs/Makefile (routines): Add wcstod_nan, wcstold_nan and
wcstof_nan.
* include/stdlib.h (__strtof_nan): Declare and use
libc_hidden_proto.
(__strtod_nan): Likewise.
(__strtold_nan): Likewise.
(__wcstof_nan): Likewise.
(__wcstod_nan): Likewise.
(__wcstold_nan): Likewise.
* include/wchar.h (____wcstoull_l_internal): Declare.
* stdlib/strtod_l.c: Do not include <ieee754.h>.
(____strtoull_l_internal): Remove declaration.
(STRTOF_NAN): Define macro.
(SET_MANTISSA): Remove macro.
(STRTOULL): Likewise.
(____STRTOF_INTERNAL): Use STRTOF_NAN to parse NaN payload.
* stdlib/strtof_l.c (____strtoull_l_internal): Remove declaration.
(STRTOF_NAN): Define macro.
(SET_MANTISSA): Remove macro.
* sysdeps/ieee754/ldbl-128/strtold_l.c (STRTOF_NAN): Define macro.
(SET_MANTISSA): Remove macro.
* sysdeps/ieee754/ldbl-128ibm/strtold_l.c (STRTOF_NAN): Define
macro.
(SET_MANTISSA): Remove macro.
* sysdeps/ieee754/ldbl-64-128/strtold_l.c (STRTOF_NAN): Define
macro.
(SET_MANTISSA): Remove macro.
* sysdeps/ieee754/ldbl-96/strtold_l.c (STRTOF_NAN): Define macro.
(SET_MANTISSA): Remove macro.
* wcsmbs/wcstod_l.c (____wcstoull_l_internal): Remove declaration.
* wcsmbs/wcstof_l.c (____wcstoull_l_internal): Likewise.
* wcsmbs/wcstold_l.c (____wcstoull_l_internal): Likewise.

[BZ #19266]
* stdlib/strtod_l.c (____STRTOF_INTERNAL): Check directly for
upper case and lower case letters inside NAN(), not using TOLOWER.
Expand Down
18 changes: 18 additions & 0 deletions include/stdlib.h
Expand Up @@ -203,6 +203,24 @@ libc_hidden_proto (strtoll)
libc_hidden_proto (strtoul)
libc_hidden_proto (strtoull)

extern float __strtof_nan (const char *, char **, char) internal_function;
extern double __strtod_nan (const char *, char **, char) internal_function;
extern long double __strtold_nan (const char *, char **, char)
internal_function;
extern float __wcstof_nan (const wchar_t *, wchar_t **, wchar_t)
internal_function;
extern double __wcstod_nan (const wchar_t *, wchar_t **, wchar_t)
internal_function;
extern long double __wcstold_nan (const wchar_t *, wchar_t **, wchar_t)
internal_function;

libc_hidden_proto (__strtof_nan)
libc_hidden_proto (__strtod_nan)
libc_hidden_proto (__strtold_nan)
libc_hidden_proto (__wcstof_nan)
libc_hidden_proto (__wcstod_nan)
libc_hidden_proto (__wcstold_nan)

extern char *__ecvt (double __value, int __ndigit, int *__restrict __decpt,
int *__restrict __sign);
extern char *__fcvt (double __value, int __ndigit, int *__restrict __decpt,
Expand Down
3 changes: 3 additions & 0 deletions include/wchar.h
Expand Up @@ -52,6 +52,9 @@ extern unsigned long long int __wcstoull_internal (const wchar_t *
__restrict __endptr,
int __base,
int __group) __THROW;
extern unsigned long long int ____wcstoull_l_internal (const wchar_t *,
wchar_t **, int, int,
__locale_t);
libc_hidden_proto (__wcstof_internal)
libc_hidden_proto (__wcstod_internal)
libc_hidden_proto (__wcstold_internal)
Expand Down
1 change: 1 addition & 0 deletions stdlib/Makefile
Expand Up @@ -50,6 +50,7 @@ routines := \
strtol_l strtoul_l strtoll_l strtoull_l \
strtof strtod strtold \
strtof_l strtod_l strtold_l \
strtof_nan strtod_nan strtold_nan \
system canonicalize \
a64l l64a \
rpmatch strfmon strfmon_l getsubopt xpg_basename fmtmsg \
Expand Down
48 changes: 9 additions & 39 deletions stdlib/strtod_l.c
Expand Up @@ -20,8 +20,6 @@
#include <xlocale.h>

extern double ____strtod_l_internal (const char *, char **, int, __locale_t);
extern unsigned long long int ____strtoull_l_internal (const char *, char **,
int, int, __locale_t);

/* Configuration part. These macros are defined by `strtold.c',
`strtof.c', `wcstod.c', `wcstold.c', and `wcstof.c' to produce the
Expand All @@ -33,27 +31,20 @@ extern unsigned long long int ____strtoull_l_internal (const char *, char **,
# ifdef USE_WIDE_CHAR
# define STRTOF wcstod_l
# define __STRTOF __wcstod_l
# define STRTOF_NAN __wcstod_nan
# else
# define STRTOF strtod_l
# define __STRTOF __strtod_l
# define STRTOF_NAN __strtod_nan
# endif
# define MPN2FLOAT __mpn_construct_double
# define FLOAT_HUGE_VAL HUGE_VAL
# define SET_MANTISSA(flt, mant) \
do { union ieee754_double u; \
u.d = (flt); \
u.ieee_nan.mantissa0 = (mant) >> 32; \
u.ieee_nan.mantissa1 = (mant); \
if ((u.ieee.mantissa0 | u.ieee.mantissa1) != 0) \
(flt) = u.d; \
} while (0)
#endif
/* End of configuration part. */

#include <ctype.h>
#include <errno.h>
#include <float.h>
#include <ieee754.h>
#include "../locale/localeinfo.h"
#include <locale.h>
#include <math.h>
Expand Down Expand Up @@ -105,7 +96,6 @@ extern unsigned long long int ____strtoull_l_internal (const char *, char **,
# define TOLOWER_C(Ch) __towlower_l ((Ch), _nl_C_locobj_ptr)
# define STRNCASECMP(S1, S2, N) \
__wcsncasecmp_l ((S1), (S2), (N), _nl_C_locobj_ptr)
# define STRTOULL(S, E, B) ____wcstoull_l_internal ((S), (E), (B), 0, loc)
#else
# define STRING_TYPE char
# define CHAR_TYPE char
Expand All @@ -117,7 +107,6 @@ extern unsigned long long int ____strtoull_l_internal (const char *, char **,
# define TOLOWER_C(Ch) __tolower_l ((Ch), _nl_C_locobj_ptr)
# define STRNCASECMP(S1, S2, N) \
__strncasecmp_l ((S1), (S2), (N), _nl_C_locobj_ptr)
# define STRTOULL(S, E, B) ____strtoull_l_internal ((S), (E), (B), 0, loc)
#endif


Expand Down Expand Up @@ -649,33 +638,14 @@ ____STRTOF_INTERNAL (const STRING_TYPE *nptr, STRING_TYPE **endptr, int group,
if (*cp == L_('('))
{
const STRING_TYPE *startp = cp;
do
++cp;
while ((*cp >= L_('0') && *cp <= L_('9'))
|| (*cp >= L_('A') && *cp <= L_('Z'))
|| (*cp >= L_('a') && *cp <= L_('z'))
|| *cp == L_('_'));

if (*cp != L_(')'))
/* The closing brace is missing. Only match the NAN
part. */
cp = startp;
STRING_TYPE *endp;
retval = STRTOF_NAN (cp + 1, &endp, L_(')'));
if (*endp == L_(')'))
/* Consume the closing parenthesis. */
cp = endp + 1;
else
{
/* This is a system-dependent way to specify the
bitmask used for the NaN. We expect it to be
a number which is put in the mantissa of the
number. */
STRING_TYPE *endp;
unsigned long long int mant;

mant = STRTOULL (startp + 1, &endp, 0);
if (endp == cp)
SET_MANTISSA (retval, mant);

/* Consume the closing brace. */
++cp;
}
/* Only match the NAN part. */
cp = startp;
}

if (endptr != NULL)
Expand Down
24 changes: 24 additions & 0 deletions stdlib/strtod_nan.c
@@ -0,0 +1,24 @@
/* Convert string for NaN payload to corresponding NaN. Narrow
strings, double.
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 <strtod_nan_narrow.h>
#include <strtod_nan_double.h>

#define STRTOD_NAN __strtod_nan
#include <strtod_nan_main.c>
30 changes: 30 additions & 0 deletions stdlib/strtod_nan_double.h
@@ -0,0 +1,30 @@
/* Convert string for NaN payload to corresponding NaN. For double.
Copyright (C) 1997-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/>. */

#define FLOAT double
#define SET_MANTISSA(flt, mant) \
do \
{ \
union ieee754_double u; \
u.d = (flt); \
u.ieee_nan.mantissa0 = (mant) >> 32; \
u.ieee_nan.mantissa1 = (mant); \
if ((u.ieee.mantissa0 | u.ieee.mantissa1) != 0) \
(flt) = u.d; \
} \
while (0)
29 changes: 29 additions & 0 deletions stdlib/strtod_nan_float.h
@@ -0,0 +1,29 @@
/* Convert string for NaN payload to corresponding NaN. For float.
Copyright (C) 1997-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/>. */

#define FLOAT float
#define SET_MANTISSA(flt, mant) \
do \
{ \
union ieee754_float u; \
u.f = (flt); \
u.ieee_nan.mantissa = (mant); \
if (u.ieee.mantissa != 0) \
(flt) = u.f; \
} \
while (0)
63 changes: 63 additions & 0 deletions stdlib/strtod_nan_main.c
@@ -0,0 +1,63 @@
/* Convert string for NaN payload to corresponding NaN.
Copyright (C) 1997-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 <ieee754.h>
#include <locale.h>
#include <math.h>
#include <stdlib.h>
#include <wchar.h>


/* If STR starts with an optional n-char-sequence as defined by ISO C
(a sequence of ASCII letters, digits and underscores), followed by
ENDC, return a NaN whose payload is set based on STR. Otherwise,
return a default NAN. If ENDPTR is not NULL, set *ENDPTR to point
to the character after the initial n-char-sequence. */

internal_function
FLOAT
STRTOD_NAN (const STRING_TYPE *str, STRING_TYPE **endptr, STRING_TYPE endc)
{
const STRING_TYPE *cp = str;

while ((*cp >= L_('0') && *cp <= L_('9'))
|| (*cp >= L_('A') && *cp <= L_('Z'))
|| (*cp >= L_('a') && *cp <= L_('z'))
|| *cp == L_('_'))
++cp;

FLOAT retval = NAN;
if (*cp != endc)
goto out;

/* This is a system-dependent way to specify the bitmask used for
the NaN. We expect it to be a number which is put in the
mantissa of the number. */
STRING_TYPE *endp;
unsigned long long int mant;

mant = STRTOULL (str, &endp, 0);
if (endp == cp)
SET_MANTISSA (retval, mant);

out:
if (endptr != NULL)
*endptr = (STRING_TYPE *) cp;
return retval;
}
libc_hidden_def (STRTOD_NAN)
22 changes: 22 additions & 0 deletions stdlib/strtod_nan_narrow.h
@@ -0,0 +1,22 @@
/* Convert string for NaN payload to corresponding NaN. Narrow strings.
Copyright (C) 1997-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/>. */

#define STRING_TYPE char
#define L_(Ch) Ch
#define STRTOULL(S, E, B) ____strtoull_l_internal ((S), (E), (B), 0, \
_nl_C_locobj_ptr)
22 changes: 22 additions & 0 deletions stdlib/strtod_nan_wide.h
@@ -0,0 +1,22 @@
/* Convert string for NaN payload to corresponding NaN. Wide strings.
Copyright (C) 1997-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/>. */

#define STRING_TYPE wchar_t
#define L_(Ch) L##Ch
#define STRTOULL(S, E, B) ____wcstoull_l_internal ((S), (E), (B), 0, \
_nl_C_locobj_ptr)

0 comments on commit e02cabe

Please sign in to comment.