Skip to content

Commit 914f9a0

Browse files
bpo-34973: Fix crash in bytes constructor. (GH-9841)
Constructing bytes from mutating list could cause a crash.
1 parent a323cdc commit 914f9a0

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
@@ -2639,49 +2639,83 @@ _PyBytes_FromBuffer(PyObject *x)
26392639
return NULL;
26402640
}
26412641

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

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

26872721
static PyObject *

0 commit comments

Comments
 (0)