8000 Issue #14700: Fix buggy overflow checks for large precision and width… · python/cpython@fb90c09 · GitHub
[go: up one dir, main page]

Skip to content

Commit fb90c09

Browse files
committed
Issue #14700: Fix buggy overflow checks for large precision and width in new-style and old-style formatting.
1 parent 579d5cd commit fb90c09

File tree

5 files changed

+45
-20
lines changed

5 files changed

+45
-20
lines changed

Lib/test/test_unicode.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -906,6 +906,21 @@ def __format__(self, spec):
906906
self.assertRaises(ValueError, '{}'.format_map, 'a')
907907
self.assertRaises(ValueError, '{a} {}'.format_map, {"a" : 2, "b" : 1})
908908

909+
def test_format_huge_precision(self):
910+
format_string = ".{}f".format(sys.maxsize + 1)
911+
with self.assertRaises(ValueError):
912+
result = format(2.34, format_string)
913+
914+
def test_format_huge_width(self):
915+
format_string = "{}f".format(sys.maxsize + 1)
916+
with self.assertRaises(ValueError):
917+
result = format(2.34, format_string)
918+
919+
def test_format_huge_item_number(self):
920+
format_string = "{{{}:.6f}}".format(sys.maxsize + 1)
921+
with self.assertRaises(ValueError):
922+
result = format_string.format(2.34)
923+
909924
def test_format_auto_numbering(self):
910925
class C:
911926
def __init__(self, x=100):
@@ -990,6 +1005,18 @@ def __str__(self):
9901005
self.assertEqual('%f' % INF, 'inf')
9911006
self.assertEqual('%F' % INF, 'INF')
9921007

1008+
@support.cpython_only
1009+
def test_formatting_huge_precision(self):
1010+
from _testcapi import INT_MAX
1011+
format_string = "%.{}f".format(INT_MAX + 1)
1012+
with self.assertRaises(ValueError):
1013+
result = format_string % 2.34
1014+
1015+
def test_formatting_huge_width(self):
1016+
format_string = "%{}f".format(sys.maxsize + 1)
1017+
with self.assertRaises(ValueError):
1018+
result = format_string % 2.34
1019+
9931020
def test_startswith_endswith_errors(self):
9941021
for meth in ('foo'.startswith, 'foo'.endswith):
9951022
with self.assertRaises(TypeError) as cm:

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ What's New in Python 3.2.4
1010
Core and Builtins
1111
-----------------
1212

13+
- Issue #14700: Fix buggy overflow checks when handling large precisions and
14+
widths in old-style and new-style formatting.
15+
1316
- Issue #6074: Ensure cached bytecode files can always be updated by the
1417
user that created them, even when the source file is read-only.
1518

Objects/stringlib/formatter.h

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ static int
7373
get_integer(STRINGLIB_CHAR **ptr, STRINGLIB_CHAR *end,
7474
Py_ssize_t *result)
7575
{
76-
Py_ssize_t accumulator, digitval, oldaccumulator;
76+
Py_ssize_t accumulator, digitval;
7777
int numdigits;
7878
accumulator = numdigits = 0;
7979
for (;;(*ptr)++, numdigits++) {
@@ -83,19 +83,17 @@ get_integer(STRINGLIB_CHAR **ptr, STRINGLIB_CHAR *end,
8383
if (digitval < 0)
8484
break;
8585
/*
86-
This trick was copied from old Unicode format code. It's cute,
87-
but would really suck on an old machine with a slow divide
88-
implementation. Fortunately, in the normal case we do not
89-
expect too many digits.
86+
Detect possible overflow before it happens:
87+
88+
accumulator * 10 + digitval > PY_SSIZE_T_MAX if and only if
89+
accumulator > (PY_SSIZE_T_MAX - digitval) / 10.
9090
*/
91-
oldaccumulator = accumulator;
92-
accumulator *= 10;
93-
if ((accumulator+10)/10 != oldaccumulator+1) {
91+
if (accumulator > (PY_SSIZE_T_MAX - digitval) / 10) {
9492
PyErr_Format(PyExc_ValueError,
9593
"Too many decimal digits in format string");
9694
return -1;
9795
}
98-
accumulator += digitval;
96+
accumulator = accumulator * 10 + digitval;
9997
}
10098
*result = accumulator;
10199
return numdigits;

Objects/stringlib/string_format.h

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,6 @@ get_integer(const SubString *str)
197197
{
198198
Py_ssize_t accumulator = 0;
199199
Py_ssize_t digitval;
200-
Py_ssize_t oldaccumulator;
201200
STRINGLIB_CHAR *p;
202201

203202
/* empty string is an error */
@@ -209,19 +208,17 @@ get_integer(const SubString *str)
209208
if (digitval < 0)
210209
return -1;
211210
/*
212-
This trick was copied from old Unicode format code. It's cute,
213-
but would really suck on an old machine with a A3E2 slow divide
214-
implementation. Fortunately, in the normal case we do not
215-
expect too many digits.
211+
Detect possible overflow before it happens:
212+
213+
accumulator * 10 + digitval > PY_SSIZE_T_MAX if and only if
214+
accumulator > (PY_SSIZE_T_MAX - digitval) / 10.
216215
*/
217-
oldaccumulator = accumulator;
218-
accumulator *= 10;
219-
if ((accumulator+10)/10 != oldaccumulator+1) {
216+
if (accumulator > (PY_SSIZE_T_MAX - digitval) / 10) {
220217
PyErr_Format(PyExc_ValueError,
221218
"Too many decimal digits in format string");
222219
return -1;
223220
}
224-
accumulator += digitval;
221+
accumulator = accumulator * 10 + digitval;
225222
}
226223
return accumulator;
227224
}

Objects/unicodeobject.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9648,7 +9648,7 @@ PyObject *PyUnicode_Format(PyObject *format,
96489648
c = *fmt++;
96499649
if (c < '0' || c > '9')
96509650
break;
9651-
if ((width*10) / 10 != width) {
9651+
if (width > (PY_SSIZE_T_MAX - ((int)c - '0')) / 10) {
96529652
PyErr_SetString(PyExc_ValueError,
96539653
"width too big");
96549654
goto onError;
@@ -9683,7 +9683,7 @@ PyObject *PyUnicode_Format(PyObject *format,
96839683
c = *fmt++;
96849684
if (c < '0' || c > '9')
96859685
break;
9686-
if ((prec*10) / 10 != prec) {
9686+
if (prec > (INT_MAX - ((int)c - '0')) / 10) {
96879687
PyErr_SetString(PyExc_ValueError,
96889688
"prec too big");
96899689
goto onError;

0 commit comments

Comments
 (0)
0