Skip to content

Commit 8bb0371

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 beb83d0 commit 8bb0371

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
@@ -2649,49 +2649,83 @@ _PyBytes_FromBuffer(PyObject *x)
26492649
return NULL;
26502650
}
26512651

2652-
#define _PyBytes_FROM_LIST_BODY(x, GET_ITEM) \
2653-
do { \
2654-
PyObject *bytes; \
2655-
Py_ssize_t i; \
2656-
Py_ssize_t value; \
2657-
char *str; \
2658-
PyObject *item; \
2659-
\
2660-
bytes = PyBytes_FromStringAndSize(NULL, Py_SIZE(x)); \
2661-
if (bytes == NULL) \
2662-
return NULL; \
2663-
str = ((PyBytesObject *)bytes)->ob_sval; \
2664-
\
2665-
for (i = 0; i < Py_SIZE(x); i++) { \
2666-
item = GET_ITEM((x), i); \
2667-
value = PyNumber_AsSsize_t(item, NULL); \
2668-
if (value == -1 && PyErr_Occurred()) \
2669-
goto error; \
2670-
\
2671-
if (value < 0 || value >= 256) { \
2672-
PyErr_SetString(PyExc_ValueError, \
2673-
"bytes must be in range(0, 256)"); \
2674-
goto error; \
2675-
} \
2676-
*str++ = (char) value; \
2677-
} \
2678-
return bytes; \
2679-
\
2680-
error: \
2681-
Py_DECREF(bytes); \
2682-
return NULL; \
2683-
} while (0)
2684-
26852652
static PyObject*
26862653
_PyBytes_FromList(PyObject *x)
26872654
{
2688-
_PyBytes_FROM_LIST_BODY(x, PyList_GET_ITEM);
2655+
Py_ssize_t i, size = PyList_GET_SIZE(x);
2656+
Py_ssize_t value;
2657+
char *str;
2658+
PyObject *item;
2659+
_PyBytesWriter writer;
2660+
2661+
_PyBytesWriter_Init(&writer);
2662+
str = _PyBytesWriter_Alloc(&writer, size);
2663+
if (str == NULL)
2664+
return NULL;
2665+
writer.overallocate = 1;
2666+
size = writer.allocated;
2667+
2668+
for (i = 0; i < PyList_GET_SIZE(x); i++) {
2669+
item = PyList_GET_ITEM(x, i);
2670+
Py_INCREF(item);
2671+
value = PyNumber_AsSsize_t(item, NULL);
2672+
Py_DECREF(item);
2673+
if (value == -1 && PyErr_Occurred())
2674+
goto error;
2675+
2676+
if (value < 0 || value >= 256) {
2677+
PyErr_SetString(PyExc_ValueError,
2678+
"bytes must be in range(0, 256)");
2679+
goto error;
2680+
}
2681+
2682+
if (i >= size) {
2683+
str = _PyBytesWriter_Resize(&writer, str, size+1);
2684+
if (str == NULL)
2685+
return NULL;
2686+
size = writer.allocated;
2687+
}
2688+
*str++ = (char) value;
2689+
}
2690+
return _PyBytesWriter_Finish(&writer, str);
2691+
2692+
error:
2693+
_PyBytesWriter_Dealloc(&writer);
2694+
return NULL;
26892695
}
26902696

26912697
static PyObject*
26922698
_PyBytes_FromTuple(PyObject *x)
26932699
{
2694-
_PyBytes_FROM_LIST_BODY(x, PyTuple_GET_ITEM);
2700+
PyObject *bytes;
2701+
Py_ssize_t i, size = PyTuple_GET_SIZE(x);
2702+
Py_ssize_t value;
2703+
char *str;
2704+
PyObject *item;
2705+
2706+
bytes = PyBytes_FromStringAndSize(NULL, size);
2707+
if (bytes == NULL)
2708+
return NULL;
2709+
str = ((PyBytesObject *)bytes)->ob_sval;
2710+
2711+
for (i = 0; i < size; i++) {
2712+
item = PyTuple_GET_ITEM(x, i);
2713+
value = PyNumber_AsSsize_t(item, NULL);
2714+
if (value == -1 && PyErr_Occurred())
2715+
goto error;
2716+
2717+
if (value < 0 || value >= 256) {
2718+
PyErr_SetString(PyExc_ValueError,
2719+
"bytes must be in range(0, 256)");
2720+
goto error;
2721+
}
2722+
*str++ = (char) value;
2723+
}
2724+
return bytes;
2725+
2726+
error:
2727+
Py_DECREF(bytes);
2728+
return NULL;
26952729
}
26962730

26972731
static PyObject *

0 commit comments

Comments
 (0)