Issue #19638: Merge from 3.3
This commit is contained in:
commit
1df88677e9
@ -248,6 +248,37 @@ class StrtodTests(unittest.TestCase):
|
|||||||
else:
|
else:
|
||||||
assert False, "expected ValueError"
|
assert False, "expected ValueError"
|
||||||
|
|
||||||
|
@test.support.bigmemtest(size=5 * test.support._1G, memuse=1, dry_run=False)
|
||||||
|
def test_oversized_digit_strings(self, maxsize):
|
||||||
|
# Input string whose length doesn't fit in an INT.
|
||||||
|
s = "1." + "1" * int(2.2e9)
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
float(s)
|
||||||
|
del s
|
||||||
|
|
||||||
|
s = "0." + "0" * int(2.2e9) + "1"
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
float(s)
|
||||||
|
del s
|
||||||
|
|
||||||
|
def test_large_exponents(self):
|
||||||
|
# Verify that the clipping of the exponent in strtod doesn't affect the
|
||||||
|
# output values.
|
||||||
|
def positive_exp(n):
|
||||||
|
""" Long string with value 1.0 and exponent n"""
|
||||||
|
return '0.{}1e+{}'.format('0'*(n-1), n)
|
||||||
|
|
||||||
|
def negative_exp(n):
|
||||||
|
""" Long string with value 1.0 and exponent -n"""
|
||||||
|
return '1{}e-{}'.format('0'*n, n)
|
||||||
|
|
||||||
|
self.assertEqual(float(positive_exp(10000)), 1.0)
|
||||||
|
self.assertEqual(float(positive_exp(20000)), 1.0)
|
||||||
|
self.assertEqual(float(positive_exp(30000)), 1.0)
|
||||||
|
self.assertEqual(float(negative_exp(10000)), 1.0)
|
||||||
|
self.assertEqual(float(negative_exp(20000)), 1.0)
|
||||||
|
self.assertEqual(float(negative_exp(30000)), 1.0)
|
||||||
|
|
||||||
def test_particular(self):
|
def test_particular(self):
|
||||||
# inputs that produced crashes or incorrectly rounded results with
|
# inputs that produced crashes or incorrectly rounded results with
|
||||||
# previous versions of dtoa.c, for various reasons
|
# previous versions of dtoa.c, for various reasons
|
||||||
|
@ -10,6 +10,9 @@ Release date: 2014-01-05
|
|||||||
Core and Builtins
|
Core and Builtins
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
- Issue #19638: Fix possible crash / undefined behaviour from huge (more than 2
|
||||||
|
billion characters) input strings in _Py_dg_strtod.
|
||||||
|
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
@ -204,7 +204,24 @@ typedef union { double d; ULong L[2]; } U;
|
|||||||
MAX_ABS_EXP in absolute value get truncated to +-MAX_ABS_EXP. MAX_ABS_EXP
|
MAX_ABS_EXP in absolute value get truncated to +-MAX_ABS_EXP. MAX_ABS_EXP
|
||||||
should fit into an int. */
|
should fit into an int. */
|
||||||
#ifndef MAX_ABS_EXP
|
#ifndef MAX_ABS_EXP
|
||||||
#define MAX_ABS_EXP 19999U
|
#define MAX_ABS_EXP 1100000000U
|
||||||
|
#endif
|
||||||
|
/* Bound on length of pieces of input strings in _Py_dg_strtod; specifically,
|
||||||
|
this is used to bound the total number of digits ignoring leading zeros and
|
||||||
|
the number of digits that follow the decimal point. Ideally, MAX_DIGITS
|
||||||
|
should satisfy MAX_DIGITS + 400 < MAX_ABS_EXP; that ensures that the
|
||||||
|
exponent clipping in _Py_dg_strtod can't affect the value of the output. */
|
||||||
|
#ifndef MAX_DIGITS
|
||||||
|
#define MAX_DIGITS 1000000000U
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Guard against trying to use the above values on unusual platforms with ints
|
||||||
|
* of width less than 32 bits. */
|
||||||
|
#if MAX_ABS_EXP > INT_MAX
|
||||||
|
#error "MAX_ABS_EXP should fit in an int"
|
||||||
|
#endif
|
||||||
|
#if MAX_DIGITS > INT_MAX
|
||||||
|
#error "MAX_DIGITS should fit in an int"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* The following definition of Storeinc is appropriate for MIPS processors.
|
/* The following definition of Storeinc is appropriate for MIPS processors.
|
||||||
@ -1538,6 +1555,7 @@ _Py_dg_strtod(const char *s00, char **se)
|
|||||||
Long L;
|
Long L;
|
||||||
BCinfo bc;
|
BCinfo bc;
|
||||||
Bigint *bb, *bb1, *bd, *bd0, *bs, *delta;
|
Bigint *bb, *bb1, *bd, *bd0, *bs, *delta;
|
||||||
|
size_t ndigits, fraclen;
|
||||||
|
|
||||||
dval(&rv) = 0.;
|
dval(&rv) = 0.;
|
||||||
|
|
||||||
@ -1560,40 +1578,53 @@ _Py_dg_strtod(const char *s00, char **se)
|
|||||||
c = *++s;
|
c = *++s;
|
||||||
lz = s != s1;
|
lz = s != s1;
|
||||||
|
|
||||||
/* Point s0 at the first nonzero digit (if any). nd0 will be the position
|
/* Point s0 at the first nonzero digit (if any). fraclen will be the
|
||||||
of the point relative to s0. nd will be the total number of digits
|
number of digits between the decimal point and the end of the
|
||||||
ignoring leading zeros. */
|
digit string. ndigits will be the total number of digits ignoring
|
||||||
|
leading zeros. */
|
||||||
s0 = s1 = s;
|
s0 = s1 = s;
|
||||||
while ('0' <= c && c <= '9')
|
while ('0' <= c && c <= '9')
|
||||||
c = *++s;
|
c = *++s;
|
||||||
nd0 = nd = s - s1;
|
ndigits = s - s1;
|
||||||
|
fraclen = 0;
|
||||||
|
|
||||||
/* Parse decimal point and following digits. */
|
/* Parse decimal point and following digits. */
|
||||||
if (c == '.') {
|
if (c == '.') {
|
||||||
c = *++s;
|
c = *++s;
|
||||||
if (!nd) {
|
if (!ndigits) {
|
||||||
s1 = s;
|
s1 = s;
|
||||||
while (c == '0')
|
while (c == '0')
|
||||||
c = *++s;
|
c = *++s;
|
||||||
lz = lz || s != s1;
|
lz = lz || s != s1;
|
||||||
nd0 -= s - s1;
|
fraclen += (s - s1);
|
||||||
s0 = s;
|
s0 = s;
|
||||||
}
|
}
|
||||||
s1 = s;
|
s1 = s;
|
||||||
while ('0' <= c && c <= '9')
|
while ('0' <= c && c <= '9')
|
||||||
c = *++s;
|
c = *++s;
|
||||||
nd += s - s1;
|
ndigits += s - s1;
|
||||||
|
fraclen += s - s1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Now lz is true if and only if there were leading zero digits, and nd
|
/* Now lz is true if and only if there were leading zero digits, and
|
||||||
gives the total number of digits ignoring leading zeros. A valid input
|
ndigits gives the total number of digits ignoring leading zeros. A
|
||||||
must have at least one digit. */
|
valid input must have at least one digit. */
|
||||||
if (!nd && !lz) {
|
if (!ndigits && !lz) {
|
||||||
if (se)
|
if (se)
|
||||||
*se = (char *)s00;
|
*se = (char *)s00;
|
||||||
goto parse_error;
|
goto parse_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Range check ndigits and fraclen to make sure that they, and values
|
||||||
|
computed with them, can safely fit in an int. */
|
||||||
|
if (ndigits > MAX_DIGITS || fraclen > MAX_DIGITS) {
|
||||||
|
if (se)
|
||||||
|
*se = (char *)s00;
|
||||||
|
goto parse_error;
|
||||||
|
}
|
||||||
|
nd = (int)ndigits;
|
||||||
|
nd0 = (int)ndigits - (int)fraclen;
|
||||||
|
|
||||||
/* Parse exponent. */
|
/* Parse exponent. */
|
||||||
e = 0;
|
e = 0;
|
||||||
if (c == 'e' || c == 'E') {
|
if (c == 'e' || c == 'E') {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user