bpo-34973: Fix crash in bytes constructor. (GH-9841)

Constructing bytes from mutating list could cause a crash.
(cherry picked from commit 914f9a078f)

Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
This commit is contained in:
Miss Islington (bot) 2018-10-21 05:55:52 -07:00 committed by GitHub
parent 002aef3f66
commit 7f34d55023
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 88 additions and 35 deletions

View File

@ -113,6 +113,23 @@ class BaseBytesTest:
b = self.type2test([1, 2, 3])
self.assertEqual(b, b"\x01\x02\x03")
def test_from_mutating_list(self):
# Issue #34973: Crash in bytes constructor with mutating list.
class X:
def __index__(self):
a.clear()
return 42
a = [X(), X()]
self.assertEqual(bytes(a), b'*')
class Y:
def __index__(self):
if len(a) < 1000:
a.append(self)
return 42
a = [Y()]
self.assertEqual(bytes(a), b'*' * 1000) # should not crash
def test_from_index(self):
b = self.type2test([Indexable(), Indexable(1), Indexable(254),
Indexable(255)])

View File

@ -0,0 +1,2 @@
Fixed crash in :func:`bytes` when the :class:`list` argument is mutated
while it is iterated.

View File

@ -2640,49 +2640,83 @@ fail:
return NULL;
}
#define _PyBytes_FROM_LIST_BODY(x, GET_ITEM) \
do { \
PyObject *bytes; \
Py_ssize_t i; \
Py_ssize_t value; \
char *str; \
PyObject *item; \
\
bytes = PyBytes_FromStringAndSize(NULL, Py_SIZE(x)); \
if (bytes == NULL) \
return NULL; \
str = ((PyBytesObject *)bytes)->ob_sval; \
\
for (i = 0; i < Py_SIZE(x); i++) { \
item = GET_ITEM((x), i); \
value = PyNumber_AsSsize_t(item, NULL); \
if (value == -1 && PyErr_Occurred()) \
goto error; \
\
if (value < 0 || value >= 256) { \
PyErr_SetString(PyExc_ValueError, \
"bytes must be in range(0, 256)"); \
goto error; \
} \
*str++ = (char) value; \
} \
return bytes; \
\
error: \
Py_DECREF(bytes); \
return NULL; \
} while (0)
static PyObject*
_PyBytes_FromList(PyObject *x)
{
_PyBytes_FROM_LIST_BODY(x, PyList_GET_ITEM);
Py_ssize_t i, size = PyList_GET_SIZE(x);
Py_ssize_t value;
char *str;
PyObject *item;
_PyBytesWriter writer;
_PyBytesWriter_Init(&writer);
str = _PyBytesWriter_Alloc(&writer, size);
if (str == NULL)
return NULL;
writer.overallocate = 1;
size = writer.allocated;
for (i = 0; i < PyList_GET_SIZE(x); i++) {
item = PyList_GET_ITEM(x, i);
Py_INCREF(item);
value = PyNumber_AsSsize_t(item, NULL);
Py_DECREF(item);
if (value == -1 && PyErr_Occurred())
goto error;
if (value < 0 || value >= 256) {
PyErr_SetString(PyExc_ValueError,
"bytes must be in range(0, 256)");
goto error;
}
if (i >= size) {
str = _PyBytesWriter_Resize(&writer, str, size+1);
if (str == NULL)
return NULL;
size = writer.allocated;
}
*str++ = (char) value;
}
return _PyBytesWriter_Finish(&writer, str);
error:
_PyBytesWriter_Dealloc(&writer);
return NULL;
}
static PyObject*
_PyBytes_FromTuple(PyObject *x)
{
_PyBytes_FROM_LIST_BODY(x, PyTuple_GET_ITEM);
PyObject *bytes;
Py_ssize_t i, size = PyTuple_GET_SIZE(x);
Py_ssize_t value;
char *str;
PyObject *item;
bytes = PyBytes_FromStringAndSize(NULL, size);
if (bytes == NULL)
return NULL;
str = ((PyBytesObject *)bytes)->ob_sval;
for (i = 0; i < size; i++) {
item = PyTuple_GET_ITEM(x, i);
value = PyNumber_AsSsize_t(item, NULL);
if (value == -1 && PyErr_Occurred())
goto error;
if (value < 0 || value >= 256) {
PyErr_SetString(PyExc_ValueError,
"bytes must be in range(0, 256)");
goto error;
}
*str++ = (char) value;
}
return bytes;
error:
Py_DECREF(bytes);
return NULL;
}
static PyObject *