Issue #24802: Merge null termination fixes from 3.4 into 3.5 · pythoncapi/cpython@61d6e4a · GitHub
Skip to content

Commit 61d6e4a

Browse files
committed
Issue python#24802: Merge null termination fixes from 3.4 into 3.5
2 parents 9b566c3 + eeb896c commit 61d6e4a

8 files changed

Lines changed: 162 additions & 37 deletions

File tree

Lib/test/test_compile.py

Lines changed: 21 additions & 0 deletions

Lib/test/test_float.py

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ def test_float(self):
3131
self.assertEqual(float(3.14), 3.14)
3232
self.assertEqual(float(314), 314.0)
3333
self.assertEqual(float(" 3.14 "), 3.14)
34-
self.assertEqual(float(b" 3.14 "), 3.14)
3534
self.assertRaises(ValueError, float, " 0x3.1 ")
3635
self.assertRaises(ValueError, float, " -0x3.p-1 ")
3736
self.assertRaises(ValueError, float, " +0x3.p-1 ")
@@ -43,7 +42,6 @@ def test_float(self):
4342
self.assertRaises(ValueError, float, "+.inf")
4443
self.assertRaises(ValueError, float, ".")
4544
self.assertRaises(ValueError, float, "-.")
46-
self.assertRaises(ValueError, float, b"-")
4745
self.assertRaises(TypeError, float, {})
4846
self.assertRaisesRegex(TypeError, "not 'dict'", float, {})
4947
# Lone surrogate
@@ -57,6 +55,42 @@ def test_float(self):
5755
float(b'.' + b'1'*1000)
5856
float('.' + '1'*1000)
5957

58+
def test_non_numeric_input_types(self):
59+
# Test possible non-numeric types for the argument x, including
60+
# subclasses of the explicitly documented accepted types.
61+
class CustomStr(str): pass
62+
class CustomBytes(bytes): pass
63+
class CustomByteArray(bytearray): pass
64+
65+
factories = [
66+
bytes,
67+
bytearray,
68+
lambda b: CustomStr(b.decode()),
69+
CustomBytes,
70+
CustomByteArray,
71+
memoryview,
72+
]
73+
try:
74+
from array import array
75+
except ImportError:
76+
pass
77+
else:
78+
factories.append(lambda b: array('B', b))
79+
80+
for f in factories:
81+
x = f(b" 3.14 ")
82+
with self.subTest(type(x)):
83+
self.assertEqual(float(x), 3.14)
84+
with self.assertRaisesRegex(ValueError, "could not convert"):
85+
float(f(b'A' * 0x10))
86+
87+
def test_float_memoryview(self):
88+
self.assertEqual(float(memoryview(b'12.3')[1:4]), 2.3)
89+
self.assertEqual(float(memoryview(b'12.3\x00')[1:4]), 2.3)
90+
self.assertEqual(float(memoryview(b'12.3 ')[1:4]), 2.3)
91+
self.assertEqual(float(memoryview(b'12.3A')[1:4]), 2.3)
92+
self.assertEqual(float(memoryview(b'12.34')[1:4]), 2.3)
93+
6094
def test_error_message(self):
6195
testlist = ('\xbd', '123\xbd', ' 123 456 ')
6296
for s in testlist:

Lib/test/test_int.py

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -276,16 +276,40 @@ class CustomStr(str): pass
276276
class CustomBytes(bytes): pass
277277
class CustomByteArray(bytearray): pass
278278

279-
values = [b'100',
280-
bytearray(b'100'),
281-
CustomStr('100'),
282-
CustomBytes(b'100'),
283-
CustomByteArray(b'100')]
284-
285-
for x in values:
286-
msg = 'x has type %s' % type(x).__name__
287-
self.assertEqual(int(x), 100, msg=msg)
288-
self.assertEqual(int(x, 2), 4, msg=msg)
279+
factories = [
280+
bytes,
281+
bytearray,
282+
lambda b: CustomStr(b.decode()),
283+
CustomBytes,
284+
CustomByteArray,
285+
memoryview,
286+
]
287+
try:
288+
from array import array
289+
except ImportError:
290+
pass
291+
else:
292+
factories.append(lambda b: array('B', b))
293+
294+
for f in factories:
295+
x = f(b'100')
296+
with self.subTest(type(x)):
297+
self.assertEqual(int(x), 100)
298+
if isinstance(x, (str, bytes, bytearray)):
299+
self.assertEqual(int(x, 2), 4)
300+
else:
301+
msg = "can't convert non-string"
302+
with self.assertRaisesRegex(TypeError, msg):
303+
int(x, 2)
304+
with self.assertRaisesRegex(ValueError, 'invalid literal'):
305+
int(f(b'A' * 0x10))
306+
307+
def test_int_memoryview(self):
308+
self.assertEqual(int(memoryview(b'123')[1:3]), 23)
309+
self.assertEqual(int(memoryview(b'123\x00')[1:3]), 23)
310+
self.assertEqual(int(memoryview(b'123 ')[1:3]), 23)
311+
self.assertEqual(int(memoryview(b'123A')[1:3]), 23)
312+
self.assertEqual(int(memoryview(b'1234')[1:3]), 23)
289313

290314
def test_string_float(self):
291315
self.assertRaises(ValueError, int, '1.2')

Misc/NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ Release date: TBA
1111
Core and Builtins
1212
-----------------
1313

14+
- Issue #24802: Avoid buffer overreads when int(), float(), compile(), exec()
15+
and eval() are passed bytes-like objects. These objects are not
16+
necessarily terminated by a null byte, but the functions assumed they were.
17+
1418
- Issue #24726: Fixed a crash and leaking NULL in repr() of OrderedDict that
1519
was mutated by direct calls of dict methods.
1620

Objects/abstract.c

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1309,12 +1309,30 @@ PyNumber_Long(PyObject *o)
13091309
/* The below check is done in PyLong_FromUnicode(). */
13101310
return PyLong_FromUnicodeObject(o, 10);
13111311

1312-
if (PyObject_GetBuffer(o, &view, PyBUF_SIMPLE) == 0) {
1312+
if (PyBytes_Check(o))
13131313
/* need to do extra error checking that PyLong_FromString()
13141314
* doesn't do. In particular int('9\x005') must raise an
13151315
* exception, not truncate at the null.
13161316
*/
1317-
PyObject *result = _PyLong_FromBytes(view.buf, view.len, 10);
1317+
return _PyLong_FromBytes(PyBytes_AS_STRING(o),
1318+
PyBytes_GET_SIZE(o), 10);
1319+
1320+
if (PyByteArray_Check(o))
1321+
return _PyLong_FromBytes(PyByteArray_AS_STRING(o),
1322+
PyByteArray_GET_SIZE(o), 10);
1323+
1324+
if (PyObject_GetBuffer(o, &view, PyBUF_SIMPLE) == 0) {
1325+
PyObject *result, *bytes;
1326+
1327+
/* Copy to NUL-terminated buffer. */
1328+
bytes = PyBytes_FromStringAndSize((const char *)view.buf, view.len);
1329+
if (bytes == NULL) {
1330+
PyBuffer_Release(&view);
1331+
return NULL;
1332+
}
1333+
result = _PyLong_FromBytes(PyBytes_AS_STRING(bytes),
1334+
PyBytes_GET_SIZE(bytes), 10);
1335+
Py_DECREF(bytes);
13181336
PyBuffer_Release(&view);
13191337
return result;
13201338
}

Objects/complexobject.c

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -767,7 +767,6 @@ complex_subtype_from_string(PyTypeObject *type, PyObject *v)
767767
int got_bracket=0;
768768
PyObject *s_buffer = NULL;
769769
Py_ssize_t len;
770-
Py_buffer view = {NULL, NULL};
771770

772771
if (PyUnicode_Check(v)) {
773772
s_buffer = _PyUnicode_TransformDecimalAndSpaceToASCII(v);
@@ -777,10 +776,6 @@ complex_subtype_from_string(PyTypeObject *type, PyObject *v)
777776
if (s == NULL)
778777
goto error;
779778
}
780-
else if (PyObject_GetBuffer(v, &view, PyBUF_SIMPLE) == 0) {
781-
s = (const char *)view.buf;
782-
len = view.len;
783-
}
784779
else {
785780
PyErr_Format(PyExc_TypeError,
786781
"complex() argument must be a string or a number, not '%.200s'",
@@ -895,15 +890,13 @@ complex_subtype_from_string(PyTypeObject *type, PyObject *v)
895890
if (s-start != len)
896891
goto parse_error;
897892

898-
PyBuffer_Release(&view);
899893
Py_XDECREF(s_buffer);
900894
return complex_subtype_from_doubles(type, x, y);
901895

902896
parse_error:
903897
PyErr_SetString(PyExc_ValueError,
904898
"complex() arg is a malformed string");
905899
error:
906-
PyBuffer_Release(&view);
907900
Py_XDECREF(s_buffer);
908901
return NULL;
909902
}

Objects/floatobject.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,9 +144,24 @@ PyFloat_FromString(PyObject *v)
144144
return NULL;
145145
}
146146
}
147+
else if (PyBytes_Check(v)) {
148+
s = PyBytes_AS_STRING(v);
149+
len = PyBytes_GET_SIZE(v);
150+
}
151+
else if (PyByteArray_Check(v)) {
152+
s = PyByteArray_AS_STRING(v);
153+
len = PyByteArray_GET_SIZE(v);
154+
}
147155
else if (PyObject_GetBuffer(v, &view, PyBUF_SIMPLE) == 0) {
148156
s = (const char *)view.buf;
149157
len = view.len;
158+
/* Copy to NUL-terminated buffer. */
159+
s_buffer = PyBytes_FromStringAndSize(s, len);
160+
if (s_buffer == NULL) {
161+
PyBuffer_Release(&view);
162+
return NULL;
163+
}
164+
s = PyBytes_AS_STRING(s_buffer);
150165
}
151166
else {
152167
PyErr_Format(PyExc_TypeError,

Python/bltinmodule.c

Lines changed: 32 additions & 16 deletions

0 commit comments

Comments
 (0)