From c057c3859c68f2e86c7a492ae8f8b4b3a3b136c8 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 3 Feb 2015 02:00:18 +0200 Subject: [PATCH] Issue #23099: Closing io.BytesIO with exported buffer is rejected now to prevent corrupting exported buffer. --- Doc/library/io.rst | 13 +++++++------ Lib/_pyio.py | 6 ++++++ Lib/test/test_memoryio.py | 7 ++++++- Misc/NEWS | 3 +++ Modules/_io/bytesio.c | 1 + 5 files changed, 23 insertions(+), 7 deletions(-) diff --git a/Doc/library/io.rst b/Doc/library/io.rst index b8dc688a630..3adf6e9c33e 100644 --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -563,7 +563,8 @@ than raw I/O does. .. class:: BytesIO([initial_bytes]) A stream implementation using an in-memory bytes buffer. It inherits - :class:`BufferedIOBase`. + :class:`BufferedIOBase`. The buffer is discarded when the + :meth:`~IOBase.close` method is called. The argument *initial_bytes* contains optional initial :class:`bytes` data. @@ -584,7 +585,7 @@ than raw I/O does. .. note:: As long as the view exists, the :class:`BytesIO` object cannot be - resized. + resized or closed. .. versionadded:: 3.2 @@ -592,6 +593,7 @@ than raw I/O does. Return :class:`bytes` containing the entire contents of the buffer. + .. method:: read1() In :class:`BytesIO`, this is the same as :meth:`read`. @@ -858,7 +860,8 @@ Text I/O .. class:: StringIO(initial_value='', newline='\\n') - An in-memory stream for text I/O. + An in-memory stream for text I/O. The text buffer is discarded when the + :meth:`~IOBase.close` method is called. The initial value of the buffer (an empty string by default) can be set by providing *initial_value*. The *newline* argument works like that of @@ -870,9 +873,7 @@ Text I/O .. method:: getvalue() - Return a ``str`` containing the entire contents of the buffer at any - time before the :class:`StringIO` object's :meth:`close` method is - called. + Return a ``str`` containing the entire contents of the buffer. Example usage:: diff --git a/Lib/_pyio.py b/Lib/_pyio.py index 01683f82859..577b6003b59 100644 --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -833,8 +833,14 @@ class BytesIO(BufferedIOBase): def getbuffer(self): """Return a readable and writable view of the buffer. """ + if self.closed: + raise ValueError("getbuffer on closed file") return memoryview(self._buffer) + def close(self): + self._buffer.clear() + super().close() + def read(self, size=None): if self.closed: raise ValueError("read from closed file") diff --git a/Lib/test/test_memoryio.py b/Lib/test/test_memoryio.py index 7cae8b29653..9a2461dea68 100644 --- a/Lib/test/test_memoryio.py +++ b/Lib/test/test_memoryio.py @@ -398,14 +398,19 @@ class BytesIOMixin: # raises a BufferError. self.assertRaises(BufferError, memio.write, b'x' * 100) self.assertRaises(BufferError, memio.truncate) + self.assertRaises(BufferError, memio.close) + self.assertFalse(memio.closed) # Mutating the buffer updates the BytesIO buf[3:6] = b"abc" self.assertEqual(bytes(buf), b"123abc7890") self.assertEqual(memio.getvalue(), b"123abc7890") - # After the buffer gets released, we can resize the BytesIO again + # After the buffer gets released, we can resize and close the BytesIO + # again del buf support.gc_collect() memio.truncate() + memio.close() + self.assertRaises(ValueError, memio.getbuffer) class PyBytesIOTest(MemoryTestMixin, MemorySeekTestMixin, diff --git a/Misc/NEWS b/Misc/NEWS index 976368ef797..6d58e8b8658 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -56,6 +56,9 @@ Core and Builtins Library ------- +- Issue #23099: Closing io.BytesIO with exported buffer is rejected now to + prevent corrupting exported buffer. + - Issue #23363: Fix possible overflow in itertools.permutations. - Issue #23364: Fix possible overflow in itertools.product. diff --git a/Modules/_io/bytesio.c b/Modules/_io/bytesio.c index 57c207341ce..1537d979a31 100644 --- a/Modules/_io/bytesio.c +++ b/Modules/_io/bytesio.c @@ -657,6 +657,7 @@ PyDoc_STRVAR(close_doc, static PyObject * bytesio_close(bytesio *self) { + CHECK_EXPORTS(self); if (self->buf != NULL) { PyMem_Free(self->buf); self->buf = NULL;