mirror of
git://sourceware.org/git/glibc.git
synced 2024-11-21 01:12:26 +08:00
Fix strtod integer/buffer overflow (bug 14459).
This commit is contained in:
parent
1f529f7d84
commit
d6e70f4368
17
ChangeLog
17
ChangeLog
@ -1,3 +1,20 @@
|
||||
2012-08-27 Joseph Myers <joseph@codesourcery.com>
|
||||
|
||||
[BZ #14459]
|
||||
* stdlib/strtod_l.c: Include <stdint.h>.
|
||||
(NDEBUG): Do not define.
|
||||
(round_and_return): Change EXPONENT parameter to type intmax_t.
|
||||
Rearrange calculations to avoid internal overflow possibilities.
|
||||
(str_to_mpn): Change EXPONENT parameter to type intmax_t *.
|
||||
Rearrange calculations to avoid internal overflow possibilities.
|
||||
Assert that number fits inside MPNSIZE limbs.
|
||||
(____STRTOF_INTERNAL): Change EXPONENT variable to type intmax_t.
|
||||
Change DIG_NO, INT_NO and LEAD_ZERO to type size_t. Rearrange
|
||||
calculations and add assertions to avoid internal overflow
|
||||
possibilities. Add casts to avoid signed/unsigned operations.
|
||||
* stdlib/tst-strtod-overflow.c: New file.
|
||||
* stdlib/Makefile (tests): Add tst-strtod-overflow.
|
||||
|
||||
2012-08-25 Marek Polacek <polacek@redhat.com>
|
||||
|
||||
* time/time.h: Fix some typos in comments.
|
||||
|
2
NEWS
2
NEWS
@ -11,7 +11,7 @@ Version 2.17
|
||||
|
||||
6778, 6808, 9685, 11607, 13717, 13696, 13939, 14042, 14090, 14166, 14150,
|
||||
14151, 14154, 14157, 14166, 14173, 14195, 14252, 14283, 14298, 14303,
|
||||
14307, 14328, 14331, 14336, 14337, 14347, 14349, 14505
|
||||
14307, 14328, 14331, 14336, 14337, 14347, 14349, 14459, 14505
|
||||
|
||||
* Support for STT_GNU_IFUNC symbols added for s390 and s390x.
|
||||
Optimized versions of memcpy, memset, and memcmp added for System z10 and
|
||||
|
@ -69,7 +69,7 @@ tests := tst-strtol tst-strtod testmb testrand testsort testdiv \
|
||||
tst-makecontext tst-strtod4 tst-strtod5 tst-qsort2 \
|
||||
tst-makecontext2 tst-strtod6 tst-unsetenv1 \
|
||||
tst-makecontext3 bug-getcontext bug-fmtmsg1 \
|
||||
tst-secure-getenv
|
||||
tst-secure-getenv tst-strtod-overflow
|
||||
tests-static := tst-secure-getenv
|
||||
|
||||
include ../Makeconfig
|
||||
|
@ -60,6 +60,7 @@ extern unsigned long long int ____strtoull_l_internal (const char *, char **,
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* The gmp headers need some configuration frobs. */
|
||||
#define HAVE_ALLOCA 1
|
||||
@ -72,7 +73,6 @@ extern unsigned long long int ____strtoull_l_internal (const char *, char **,
|
||||
#include "longlong.h"
|
||||
#include "fpioconst.h"
|
||||
|
||||
#define NDEBUG 1
|
||||
#include <assert.h>
|
||||
|
||||
|
||||
@ -174,19 +174,19 @@ extern const mp_limb_t _tens_in_limb[MAX_DIG_PER_LIMB + 1];
|
||||
/* Return a floating point number of the needed type according to the given
|
||||
multi-precision number after possible rounding. */
|
||||
static FLOAT
|
||||
round_and_return (mp_limb_t *retval, int exponent, int negative,
|
||||
round_and_return (mp_limb_t *retval, intmax_t exponent, int negative,
|
||||
mp_limb_t round_limb, mp_size_t round_bit, int more_bits)
|
||||
{
|
||||
if (exponent < MIN_EXP - 1)
|
||||
{
|
||||
mp_size_t shift = MIN_EXP - 1 - exponent;
|
||||
|
||||
if (shift > MANT_DIG)
|
||||
if (exponent < MIN_EXP - 1 - MANT_DIG)
|
||||
{
|
||||
__set_errno (ERANGE);
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
mp_size_t shift = MIN_EXP - 1 - exponent;
|
||||
|
||||
more_bits |= (round_limb & ((((mp_limb_t) 1) << round_bit) - 1)) != 0;
|
||||
if (shift == MANT_DIG)
|
||||
/* This is a special case to handle the very seldom case where
|
||||
@ -233,6 +233,9 @@ round_and_return (mp_limb_t *retval, int exponent, int negative,
|
||||
__set_errno (ERANGE);
|
||||
}
|
||||
|
||||
if (exponent > MAX_EXP)
|
||||
goto overflow;
|
||||
|
||||
if ((round_limb & (((mp_limb_t) 1) << round_bit)) != 0
|
||||
&& (more_bits || (retval[0] & 1) != 0
|
||||
|| (round_limb & ((((mp_limb_t) 1) << round_bit) - 1)) != 0))
|
||||
@ -258,6 +261,7 @@ round_and_return (mp_limb_t *retval, int exponent, int negative,
|
||||
}
|
||||
|
||||
if (exponent > MAX_EXP)
|
||||
overflow:
|
||||
return negative ? -FLOAT_HUGE_VAL : FLOAT_HUGE_VAL;
|
||||
|
||||
return MPN2FLOAT (retval, exponent, negative);
|
||||
@ -271,7 +275,7 @@ round_and_return (mp_limb_t *retval, int exponent, int negative,
|
||||
factor for the resulting number (see code) multiply by it. */
|
||||
static const STRING_TYPE *
|
||||
str_to_mpn (const STRING_TYPE *str, int digcnt, mp_limb_t *n, mp_size_t *nsize,
|
||||
int *exponent
|
||||
intmax_t *exponent
|
||||
#ifndef USE_WIDE_CHAR
|
||||
, const char *decimal, size_t decimal_len, const char *thousands
|
||||
#endif
|
||||
@ -301,6 +305,7 @@ str_to_mpn (const STRING_TYPE *str, int digcnt, mp_limb_t *n, mp_size_t *nsize,
|
||||
cy += __mpn_add_1 (n, n, *nsize, low);
|
||||
if (cy != 0)
|
||||
{
|
||||
assert (*nsize < MPNSIZE);
|
||||
n[*nsize] = cy;
|
||||
++(*nsize);
|
||||
}
|
||||
@ -335,7 +340,7 @@ str_to_mpn (const STRING_TYPE *str, int digcnt, mp_limb_t *n, mp_size_t *nsize,
|
||||
}
|
||||
while (--digcnt > 0);
|
||||
|
||||
if (*exponent > 0 && cnt + *exponent <= MAX_DIG_PER_LIMB)
|
||||
if (*exponent > 0 && *exponent <= MAX_DIG_PER_LIMB - cnt)
|
||||
{
|
||||
low *= _tens_in_limb[*exponent];
|
||||
start = _tens_in_limb[cnt + *exponent];
|
||||
@ -355,7 +360,10 @@ str_to_mpn (const STRING_TYPE *str, int digcnt, mp_limb_t *n, mp_size_t *nsize,
|
||||
cy = __mpn_mul_1 (n, n, *nsize, start);
|
||||
cy += __mpn_add_1 (n, n, *nsize, low);
|
||||
if (cy != 0)
|
||||
n[(*nsize)++] = cy;
|
||||
{
|
||||
assert (*nsize < MPNSIZE);
|
||||
n[(*nsize)++] = cy;
|
||||
}
|
||||
}
|
||||
|
||||
return str;
|
||||
@ -413,7 +421,7 @@ ____STRTOF_INTERNAL (nptr, endptr, group, loc)
|
||||
{
|
||||
int negative; /* The sign of the number. */
|
||||
MPN_VAR (num); /* MP representation of the number. */
|
||||
int exponent; /* Exponent of the number. */
|
||||
intmax_t exponent; /* Exponent of the number. */
|
||||
|
||||
/* Numbers starting `0X' or `0x' have to be processed with base 16. */
|
||||
int base = 10;
|
||||
@ -435,7 +443,7 @@ ____STRTOF_INTERNAL (nptr, endptr, group, loc)
|
||||
/* Points at the character following the integer and fractional digits. */
|
||||
const STRING_TYPE *expp;
|
||||
/* Total number of digit and number of digits in integer part. */
|
||||
int dig_no, int_no, lead_zero;
|
||||
size_t dig_no, int_no, lead_zero;
|
||||
/* Contains the last character read. */
|
||||
CHAR_TYPE c;
|
||||
|
||||
@ -767,7 +775,7 @@ ____STRTOF_INTERNAL (nptr, endptr, group, loc)
|
||||
are all or any is really a fractional digit will be decided
|
||||
later. */
|
||||
int_no = dig_no;
|
||||
lead_zero = int_no == 0 ? -1 : 0;
|
||||
lead_zero = int_no == 0 ? (size_t) -1 : 0;
|
||||
|
||||
/* Read the fractional digits. A special case are the 'american
|
||||
style' numbers like `16.' i.e. with decimal point but without
|
||||
@ -789,12 +797,13 @@ ____STRTOF_INTERNAL (nptr, endptr, group, loc)
|
||||
(base == 16 && ({ CHAR_TYPE lo = TOLOWER (c);
|
||||
lo >= L_('a') && lo <= L_('f'); })))
|
||||
{
|
||||
if (c != L_('0') && lead_zero == -1)
|
||||
if (c != L_('0') && lead_zero == (size_t) -1)
|
||||
lead_zero = dig_no - int_no;
|
||||
++dig_no;
|
||||
c = *++cp;
|
||||
}
|
||||
}
|
||||
assert (dig_no <= (uintmax_t) INTMAX_MAX);
|
||||
|
||||
/* Remember start of exponent (if any). */
|
||||
expp = cp;
|
||||
@ -817,24 +826,80 @@ ____STRTOF_INTERNAL (nptr, endptr, group, loc)
|
||||
|
||||
if (c >= L_('0') && c <= L_('9'))
|
||||
{
|
||||
int exp_limit;
|
||||
intmax_t exp_limit;
|
||||
|
||||
/* Get the exponent limit. */
|
||||
if (base == 16)
|
||||
exp_limit = (exp_negative ?
|
||||
-MIN_EXP + MANT_DIG + 4 * int_no :
|
||||
MAX_EXP - 4 * int_no + 4 * lead_zero + 3);
|
||||
{
|
||||
if (exp_negative)
|
||||
{
|
||||
assert (int_no <= (uintmax_t) (INTMAX_MAX
|
||||
+ MIN_EXP - MANT_DIG) / 4);
|
||||
exp_limit = -MIN_EXP + MANT_DIG + 4 * (intmax_t) int_no;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (int_no)
|
||||
{
|
||||
assert (lead_zero == 0
|
||||
&& int_no <= (uintmax_t) INTMAX_MAX / 4);
|
||||
exp_limit = MAX_EXP - 4 * (intmax_t) int_no + 3;
|
||||
}
|
||||
else if (lead_zero == (size_t) -1)
|
||||
{
|
||||
/* The number is zero and this limit is
|
||||
arbitrary. */
|
||||
exp_limit = MAX_EXP + 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert (lead_zero
|
||||
<= (uintmax_t) (INTMAX_MAX - MAX_EXP - 3) / 4);
|
||||
exp_limit = (MAX_EXP
|
||||
+ 4 * (intmax_t) lead_zero
|
||||
+ 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
exp_limit = (exp_negative ?
|
||||
-MIN_10_EXP + MANT_DIG + int_no :
|
||||
MAX_10_EXP - int_no + lead_zero + 1);
|
||||
{
|
||||
if (exp_negative)
|
||||
{
|
||||
assert (int_no
|
||||
<= (uintmax_t) (INTMAX_MAX + MIN_10_EXP - MANT_DIG));
|
||||
exp_limit = -MIN_10_EXP + MANT_DIG + (intmax_t) int_no;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (int_no)
|
||||
{
|
||||
assert (lead_zero == 0
|
||||
&& int_no <= (uintmax_t) INTMAX_MAX);
|
||||
exp_limit = MAX_10_EXP - (intmax_t) int_no + 1;
|
||||
}
|
||||
else if (lead_zero == (size_t) -1)
|
||||
{
|
||||
/* The number is zero and this limit is
|
||||
arbitrary. */
|
||||
exp_limit = MAX_10_EXP + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert (lead_zero
|
||||
<= (uintmax_t) (INTMAX_MAX - MAX_10_EXP - 1));
|
||||
exp_limit = MAX_10_EXP + (intmax_t) lead_zero + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (exp_limit < 0)
|
||||
exp_limit = 0;
|
||||
|
||||
do
|
||||
{
|
||||
exponent *= 10;
|
||||
exponent += c - L_('0');
|
||||
|
||||
if (__builtin_expect (exponent > exp_limit, 0))
|
||||
if (__builtin_expect ((exponent > exp_limit / 10
|
||||
|| (exponent == exp_limit / 10
|
||||
&& c - L_('0') > exp_limit % 10)), 0))
|
||||
/* The exponent is too large/small to represent a valid
|
||||
number. */
|
||||
{
|
||||
@ -843,7 +908,7 @@ ____STRTOF_INTERNAL (nptr, endptr, group, loc)
|
||||
/* We have to take care for special situation: a joker
|
||||
might have written "0.0e100000" which is in fact
|
||||
zero. */
|
||||
if (lead_zero == -1)
|
||||
if (lead_zero == (size_t) -1)
|
||||
result = negative ? -0.0 : 0.0;
|
||||
else
|
||||
{
|
||||
@ -862,6 +927,9 @@ ____STRTOF_INTERNAL (nptr, endptr, group, loc)
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
exponent *= 10;
|
||||
exponent += c - L_('0');
|
||||
|
||||
c = *++cp;
|
||||
}
|
||||
while (c >= L_('0') && c <= L_('9'));
|
||||
@ -930,7 +998,14 @@ ____STRTOF_INTERNAL (nptr, endptr, group, loc)
|
||||
}
|
||||
#endif
|
||||
startp += lead_zero + decimal_len;
|
||||
exponent -= base == 16 ? 4 * lead_zero : lead_zero;
|
||||
assert (lead_zero <= (base == 16
|
||||
? (uintmax_t) INTMAX_MAX / 4
|
||||
: (uintmax_t) INTMAX_MAX));
|
||||
assert (lead_zero <= (base == 16
|
||||
? ((uintmax_t) exponent
|
||||
- (uintmax_t) INTMAX_MIN) / 4
|
||||
: ((uintmax_t) exponent - (uintmax_t) INTMAX_MIN)));
|
||||
exponent -= base == 16 ? 4 * (intmax_t) lead_zero : (intmax_t) lead_zero;
|
||||
dig_no -= lead_zero;
|
||||
}
|
||||
|
||||
@ -972,7 +1047,10 @@ ____STRTOF_INTERNAL (nptr, endptr, group, loc)
|
||||
}
|
||||
|
||||
/* Adjust the exponent for the bits we are shifting in. */
|
||||
exponent += bits - 1 + (int_no - 1) * 4;
|
||||
assert (int_no <= (uintmax_t) (exponent < 0
|
||||
? (INTMAX_MAX - bits + 1) / 4
|
||||
: (INTMAX_MAX - exponent - bits + 1) / 4));
|
||||
exponent += bits - 1 + ((intmax_t) int_no - 1) * 4;
|
||||
|
||||
while (--dig_no > 0 && idx >= 0)
|
||||
{
|
||||
@ -1024,13 +1102,15 @@ ____STRTOF_INTERNAL (nptr, endptr, group, loc)
|
||||
really integer digits or belong to the fractional part; i.e. we normalize
|
||||
123e-2 to 1.23. */
|
||||
{
|
||||
register int incr = (exponent < 0 ? MAX (-int_no, exponent)
|
||||
: MIN (dig_no - int_no, exponent));
|
||||
register intmax_t incr = (exponent < 0
|
||||
? MAX (-(intmax_t) int_no, exponent)
|
||||
: MIN ((intmax_t) dig_no - (intmax_t) int_no,
|
||||
exponent));
|
||||
int_no += incr;
|
||||
exponent -= incr;
|
||||
}
|
||||
|
||||
if (__builtin_expect (int_no + exponent > MAX_10_EXP + 1, 0))
|
||||
if (__builtin_expect (exponent > MAX_10_EXP + 1 - (intmax_t) int_no, 0))
|
||||
{
|
||||
__set_errno (ERANGE);
|
||||
return negative ? -FLOAT_HUGE_VAL : FLOAT_HUGE_VAL;
|
||||
@ -1215,7 +1295,7 @@ ____STRTOF_INTERNAL (nptr, endptr, group, loc)
|
||||
digits we should have enough bits for the result. The remaining
|
||||
decimal digits give us the information that more bits are following.
|
||||
This can be used while rounding. (Two added as a safety margin.) */
|
||||
if (dig_no - int_no > (MANT_DIG - bits + 2) / 3 + 2)
|
||||
if ((intmax_t) dig_no > (intmax_t) int_no + (MANT_DIG - bits + 2) / 3 + 2)
|
||||
{
|
||||
dig_no = int_no + (MANT_DIG - bits + 2) / 3 + 2;
|
||||
more_bits = 1;
|
||||
@ -1223,7 +1303,7 @@ ____STRTOF_INTERNAL (nptr, endptr, group, loc)
|
||||
else
|
||||
more_bits = 0;
|
||||
|
||||
neg_exp = dig_no - int_no - exponent;
|
||||
neg_exp = (intmax_t) dig_no - (intmax_t) int_no - exponent;
|
||||
|
||||
/* Construct the denominator. */
|
||||
densize = 0;
|
||||
|
48
stdlib/tst-strtod-overflow.c
Normal file
48
stdlib/tst-strtod-overflow.c
Normal file
@ -0,0 +1,48 @@
|
||||
/* Test for integer/buffer overflow in strtod.
|
||||
Copyright (C) 2012 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define EXPONENT "e-2147483649"
|
||||
#define SIZE 214748364
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
char *p = malloc (1 + SIZE + sizeof (EXPONENT));
|
||||
if (p == NULL)
|
||||
{
|
||||
puts ("malloc failed, cannot test for overflow");
|
||||
return 0;
|
||||
}
|
||||
p[0] = '1';
|
||||
memset (p + 1, '0', SIZE);
|
||||
memcpy (p + 1 + SIZE, EXPONENT, sizeof (EXPONENT));
|
||||
double d = strtod (p, NULL);
|
||||
if (d != 0)
|
||||
{
|
||||
printf ("strtod returned wrong value: %a\n", d);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define TEST_FUNCTION do_test ()
|
||||
#include "../test-skeleton.c"
|
Loading…
Reference in New Issue
Block a user