Skip to content

Commit d2d8ed2

Browse files
pythongh-111049: Fix crash during garbage collection of the BytesIO buffer object
1 parent b845a9e commit d2d8ed2

File tree

3 files changed

+28
-10
lines changed

3 files changed

+28
-10
lines changed

Lib/test/test_memoryio.py

+21
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@
66
import unittest
77
from test import support
88

9+
import gc
910
import io
1011
import _pyio as pyio
1112
import pickle
1213
import sys
14+
import weakref
1315

1416
class IntLike:
1517
def __init__(self, num):
@@ -463,6 +465,25 @@ def test_getbuffer(self):
463465
memio.close()
464466
self.assertRaises(ValueError, memio.getbuffer)
465467

468+
def test_getbuffer_gc_collect(self):
469+
memio = self.ioclass(b"1234567890")
470+
buf = memio.getbuffer()
471+
memiowr = weakref.ref(memio)
472+
bufwr = weakref.ref(buf)
473+
# Create a reference loop.
474+
a = [buf]
475+
a.append(a)
476+
# XXX The Python implementation emits an unraisable exception.
477+
with support.catch_unraisable_exception():
478+
del memio
479+
del buf
480+
del a
481+
# XXX The C implementation emits an unraisable exception.
482+
with support.catch_unraisable_exception():
483+
gc.collect()
484+
self.assertIsNone(memiowr())
485+
self.assertIsNone(bufwr())
486+
466487
def test_read1(self):
467488
buf = self.buftype("1234567890")
468489
self.assertEqual(self.ioclass(buf).read1(), buf)
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

+5-10
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ check_exports(bytesio *self)
4949
if (self->exports > 0) {
5050
PyErr_SetString(PyExc_BufferError,
5151
"Existing exports of data: object cannot be re-sized");
52+
// abort();
5253
return 1;
5354
}
5455
return 0;
@@ -989,7 +990,9 @@ static int
989990
bytesio_clear(bytesio *self)
990991
{
991992
Py_CLEAR(self->dict);
992-
Py_CLEAR(self->buf);
993+
if (self->exports == 0) {
994+
Py_CLEAR(self->buf);
995+
}
993996
return 0;
994997
}
995998

@@ -1094,13 +1097,6 @@ bytesiobuf_releasebuffer(bytesiobuf *obj, Py_buffer *view)
10941097
b->exports--;
10951098
}
10961099

1097-
static int
1098-
bytesiobuf_clear(bytesiobuf *self)
1099-
{
1100-
Py_CLEAR(self->source);
1101-
return 0;
1102-
}
1103-
11041100
static int
11051101
bytesiobuf_traverse(bytesiobuf *self, visitproc visit, void *arg)
11061102
{
@@ -1115,15 +1111,14 @@ bytesiobuf_dealloc(bytesiobuf *self)
11151111
PyTypeObject *tp = Py_TYPE(self);
11161112
/* bpo-31095: UnTrack is needed before calling any callbacks */
11171113
PyObject_GC_UnTrack(self);
1118-
(void)bytesiobuf_clear(self);
1114+
Py_CLEAR(self->source);
11191115
tp->tp_free(self);
11201116
Py_DECREF(tp);
11211117
}
11221118

11231119
static PyType_Slot bytesiobuf_slots[] = {
11241120
{Py_tp_dealloc, bytesiobuf_dealloc},
11251121
{Py_tp_traverse, bytesiobuf_traverse},
1126-
{Py_tp_clear, bytesiobuf_clear},
11271122

11281123
// Buffer protocol
11291124
{Py_bf_getbuffer, bytesiobuf_getbuffer},

0 commit comments

Comments
 (0)