Skip to content

Commit 7f34d55

Browse files
bpo-34973: Fix crash in bytes constructor. (GH-9841)
Constructing bytes from mutating list could cause a crash. (cherry picked from commit 914f9a0) Co-authored-by: Serhiy Storchaka <[email protected]>
1 parent 002aef3 commit 7f34d55

File tree

3 files changed

+88
-35
lines changed

3 files changed

+88
-35
lines changed

Lib/test/test_bytes.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,23 @@ def test_from_list(self):
113113
b = self.type2test([1, 2, 3])
114114
self.assertEqual(b, b"\x01\x02\x03")
115115

116+
def test_from_mutating_list(self):
117+
# Issue #34973: Crash in bytes constructor with mutating list.
118+
class X:
119+
def __index__(self):
120+
a.clear()
121+
return 42
122+
a = [X(), X()]
123+
self.assertEqual(bytes(a), b'*')
124+
125+
class Y:
126+
def __index__(self):
127+
if len(a) < 1000:
128+
a.append(self)
129+
return 42
130+
a = [Y()]
131+
self.assertEqual(bytes(a), b'*' * 1000) # should not crash
132+
116133
def test_from_index(self):
117134
b = self.type2test([Indexable(), Indexable(1), Indexable(254),
118135
Indexable(255)])
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fixed crash in :func:`bytes` when the :class:`list` argument is mutated
2+
while it is iterated.

Objects/bytesobject.c

Lines changed: 69 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2640,49 +2640,83 @@ _PyBytes_FromBuffer(PyObject *x)
26402640
return NULL;
26412641
}
26422642

2643-
#define _PyBytes_FROM_LIST_BODY(x, GET_ITEM) \
2644-
do { \
2645-
PyObject *bytes; \
2646-
Py_ssize_t i; \
2647-
Py_ssize_t value; \
2648-
char *str; \
2649-
PyObject *item; \
2650-
\
2651-
bytes = PyBytes_FromStringAndSize(NULL, Py_SIZE(x)); \
2652-
if (bytes == NULL) \
2653-
return NULL; \
2654-
str = ((PyBytesObject *)bytes)->ob_sval; \
2655-
\
2656-
for (i = 0; i < Py_SIZE(x); i++) { \
2657-
item = GET_ITEM((x), i); \
2658-
value = PyNumber_AsSsize_t(item, NULL); \
2659-
if (value == -1 && PyErr_Occurred()) \
2660-
goto error; \
2661-
\
2662-
if (value < 0 || value >= 256) { \
2663-
PyErr_SetString(PyExc_ValueError, \
2664-
"bytes must be in range(0, 256)"); \
2665-
goto error; \
2666-
} \
2667-
*str++ = (char) value; \
2668-
} \
2669-
return bytes; \
2670-
\
2671-
error: \
2672-
Py_DECREF(bytes); \
2673-
return NULL; \
2674-
} while (0)
2675-
26762643
static PyObject*
26772644
_PyBytes_FromList(PyObject *x)
26782645
{
2679-
_PyBytes_FROM_LIST_BODY(x, PyList_GET_ITEM);
2646+
Py_ssize_t i, size = PyList_GET_SIZE(x);
2647+
Py_ssize_t value;
2648+
char *str;
2649+
PyObject *item;
2650+
_PyBytesWriter writer;
2651+
2652+
_PyBytesWriter_Init(&writer);
2653+
str = _PyBytesWriter_Alloc(&writer, size);
2654+
if (str == NULL)
2655+
return NULL;
2656+
writer.overallocate = 1;
2657+
size = writer.allocated;
2658+
2659+
for (i = 0; i < PyList_GET_SIZE(x); i++) {
2660+
item = PyList_GET_ITEM(x, i);
2661+
Py_INCREF(item);
2662+
value = PyNumber_AsSsize_t(item, NULL);
2663+
Py_DECREF(item);
2664+
if (value == -1 && PyErr_Occurred())
2665+
goto error;
2666+
2667+
if (value < 0 || value >= 256) {
2668+
PyErr_SetString(PyExc_ValueError,
2669+
"bytes must be in range(0, 256)");
2670+
goto error;
2671+
}
2672+
2673+
if (i >= size) {
2674+
str = _PyBytesWriter_Resize(&writer, str, size+1);
2675+
if (str == NULL)
2676+
return NULL;
2677+
size = writer.allocated;
2678+
}
2679+
*str++ = (char) value;
2680+
}
2681+
return _PyBytesWriter_Finish(&writer, str);
2682+
2683+
error:
2684+
_PyBytesWriter_Dealloc(&writer);
2685+
return NULL;
26802686
}
26812687

26822688
static PyObject*
26832689
_PyBytes_FromTuple(PyObject *x)
26842690
{
2685-
_PyBytes_FROM_LIST_BODY(x, PyTuple_GET_ITEM);
2691+
PyObject *bytes;
2692+
Py_ssize_t i, size = PyTuple_GET_SIZE(x);
2693+
Py_ssize_t value;
2694+
char *str;
2695+
PyObject *item;
2696+
2697+
bytes = PyBytes_FromStringAndSize(NULL, size);
2698+
if (bytes == NULL)
2699+
return NULL;
2700+
str = ((PyBytesObject *)bytes)->ob_sval;
2701+
2702+
for (i = 0; i < size; i++) {
2703+
item = PyTuple_GET_ITEM(x, i);
2704+
value = PyNumber_AsSsize_t(item, NULL);
2705+
if (value == -1 && PyErr_Occurred())
2706+
goto error;
2707+
2708+
if (value < 0 || value >= 256) {
2709+
PyErr_SetString(PyExc_ValueError,
2710+
"bytes must be in range(0, 256)");
2711+
goto error;
2712+
}
2713+
*str++ = (char) value;
2714+
}
2715+
return bytes;
2716+
2717+
error:
2718+
Py_DECREF(bytes);
2719+
return NULL;
26862720
}
26872721

26882722
static PyObject *

0 commit comments

Comments
 (0)