Skip to content

Commit bb36f72

Browse files
gh-111049: Fix crash during garbage collection of the BytesIO buffer object (GH-111221)
1 parent 4d5d9ac commit bb36f72

3 files changed

Lines changed: 27 additions & 10 deletions

File tree

‎Lib/test/test_memoryio.py‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@
66
importunittest
77
fromtestimportsupport
88

9+
importgc
910
importio
1011
import_pyioaspyio
1112
importpickle
1213
importsys
14+
importweakref
1315

1416
classIntLike:
1517
def__init__(self, num):
@@ -477,6 +479,25 @@ def test_getbuffer_empty(self):
477479
buf2.release()
478480
memio.write(b'x')
479481

482+
deftest_getbuffer_gc_collect(self):
483+
memio=self.ioclass(b"1234567890")
484+
buf=memio.getbuffer()
485+
memiowr=weakref.ref(memio)
486+
bufwr=weakref.ref(buf)
487+
# Create a reference loop.
488+
a= [buf]
489+
a.append(a)
490+
# The Python implementation emits an unraisable exception.
491+
withsupport.catch_unraisable_exception():
492+
delmemio
493+
delbuf
494+
dela
495+
# The C implementation emits an unraisable exception.
496+
withsupport.catch_unraisable_exception():
497+
gc.collect()
498+
self.assertIsNone(memiowr())
499+
self.assertIsNone(bufwr())
500+
480501
deftest_read1(self):
481502
buf=self.buftype("1234567890")
482503
self.assertEqual(self.ioclass(buf).read1(), buf)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix crash during garbage collection of the :class:`io.BytesIO` buffer
2+
object.

‎Modules/_io/bytesio.c‎

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -990,7 +990,9 @@ static int
990990
bytesio_clear(bytesio*self)
991991
{
992992
Py_CLEAR(self->dict);
993-
Py_CLEAR(self->buf);
993+
if (self->exports==0) {
994+
Py_CLEAR(self->buf);
995+
}
994996
return0;
995997
}
996998

@@ -1095,13 +1097,6 @@ bytesiobuf_releasebuffer(bytesiobuf *obj, Py_buffer *view)
10951097
b->exports--;
10961098
}
10971099

1098-
staticint
1099-
bytesiobuf_clear(bytesiobuf*self)
1100-
{
1101-
Py_CLEAR(self->source);
1102-
return0;
1103-
}
1104-
11051100
staticint
11061101
bytesiobuf_traverse(bytesiobuf*self, visitprocvisit, void*arg)
11071102
{
@@ -1116,15 +1111,14 @@ bytesiobuf_dealloc(bytesiobuf *self)
11161111
PyTypeObject*tp=Py_TYPE(self);
11171112
/* bpo-31095: UnTrack is needed before calling any callbacks */
11181113
PyObject_GC_UnTrack(self);
1119-
(void)bytesiobuf_clear(self);
1114+
Py_CLEAR(self->source);
11201115
tp->tp_free(self);
11211116
Py_DECREF(tp);
11221117
}
11231118

11241119
staticPyType_Slotbytesiobuf_slots[] = {
11251120
{Py_tp_dealloc, bytesiobuf_dealloc},
11261121
{Py_tp_traverse, bytesiobuf_traverse},
1127-
{Py_tp_clear, bytesiobuf_clear},
11281122

11291123
// Buffer protocol
11301124
{Py_bf_getbuffer, bytesiobuf_getbuffer},

0 commit comments

Comments
 (0)