From bb36f72efcc6a656e0907ffa83620a1e44044895 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 14 Dec 2023 12:04:23 +0200 Subject: gh-111049: Fix crash during garbage collection of the BytesIO buffer object (GH-111221) --- Lib/test/test_memoryio.py | 21 +++++++++++++++++++++ .../2023-10-23-18-42-26.gh-issue-111049.Ys7-o_.rst | 2 ++ Modules/_io/bytesio.c | 14 ++++---------- 3 files changed, 27 insertions(+), 10 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-10-23-18-42-26.gh-issue-111049.Ys7-o_.rst diff --git a/Lib/test/test_memoryio.py b/Lib/test/test_memoryio.py index 7312992..8192502 100644 --- a/Lib/test/test_memoryio.py +++ b/Lib/test/test_memoryio.py @@ -6,10 +6,12 @@ BytesIO -- for bytes import unittest from test import support +import gc import io import _pyio as pyio import pickle import sys +import weakref class IntLike: def __init__(self, num): @@ -477,6 +479,25 @@ class PyBytesIOTest(MemoryTestMixin, MemorySeekTestMixin, unittest.TestCase): buf2.release() memio.write(b'x') + def test_getbuffer_gc_collect(self): + memio = self.ioclass(b"1234567890") + buf = memio.getbuffer() + memiowr = weakref.ref(memio) + bufwr = weakref.ref(buf) + # Create a reference loop. + a = [buf] + a.append(a) + # The Python implementation emits an unraisable exception. + with support.catch_unraisable_exception(): + del memio + del buf + del a + # The C implementation emits an unraisable exception. + with support.catch_unraisable_exception(): + gc.collect() + self.assertIsNone(memiowr()) + self.assertIsNone(bufwr()) + def test_read1(self): buf = self.buftype("1234567890") self.assertEqual(self.ioclass(buf).read1(), buf) diff --git a/Misc/NEWS.d/next/Library/2023-10-23-18-42-26.gh-issue-111049.Ys7-o_.rst b/Misc/NEWS.d/next/Library/2023-10-23-18-42-26.gh-issue-111049.Ys7-o_.rst new file mode 100644 index 0000000..b1de348 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-10-23-18-42-26.gh-issue-111049.Ys7-o_.rst @@ -0,0 +1,2 @@ +Fix crash during garbage collection of the :class:`io.BytesIO` buffer +object. diff --git a/Modules/_io/bytesio.c b/Modules/_io/bytesio.c index 16b8ac6..4a15c8e 100644 --- a/Modules/_io/bytesio.c +++ b/Modules/_io/bytesio.c @@ -990,7 +990,9 @@ static int bytesio_clear(bytesio *self) { Py_CLEAR(self->dict); - Py_CLEAR(self->buf); + if (self->exports == 0) { + Py_CLEAR(self->buf); + } return 0; } @@ -1096,13 +1098,6 @@ bytesiobuf_releasebuffer(bytesiobuf *obj, Py_buffer *view) } static int -bytesiobuf_clear(bytesiobuf *self) -{ - Py_CLEAR(self->source); - return 0; -} - -static int bytesiobuf_traverse(bytesiobuf *self, visitproc visit, void *arg) { Py_VISIT(Py_TYPE(self)); @@ -1116,7 +1111,7 @@ bytesiobuf_dealloc(bytesiobuf *self) PyTypeObject *tp = Py_TYPE(self); /* bpo-31095: UnTrack is needed before calling any callbacks */ PyObject_GC_UnTrack(self); - (void)bytesiobuf_clear(self); + Py_CLEAR(self->source); tp->tp_free(self); Py_DECREF(tp); } @@ -1124,7 +1119,6 @@ bytesiobuf_dealloc(bytesiobuf *self) static PyType_Slot bytesiobuf_slots[] = { {Py_tp_dealloc, bytesiobuf_dealloc}, {Py_tp_traverse, bytesiobuf_traverse}, - {Py_tp_clear, bytesiobuf_clear}, // Buffer protocol {Py_bf_getbuffer, bytesiobuf_getbuffer}, -- cgit v0.12