Skip to content

Commit 9e6929a

Browse files
ngoldbaummiss-islington
authored andcommitted
gh-157048: check for buffer errors before mutating in BytesIO.__init__ (GH-157049)
(cherry picked from commit d125f00) Co-authored-by: Nathan Goldbaum <nathan.goldbaum@gmail.com>
1 parent 3d16dc6 commit 9e6929a

3 files changed

Lines changed: 19 additions & 4 deletions

File tree

Lib/test/test_io/test_memoryio.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -923,6 +923,18 @@ def test_cow_mutable(self):
923923
memio = self.ioclass(ba)
924924
self.assertEqual(sys.getrefcount(ba), old_rc)
925925

926+
def test_write_with_export(self):
927+
memio = self.ioclass(b"abcd")
928+
memio.seek(2)
929+
with memio.getbuffer() as view:
930+
self.assertRaises(BufferError, memio.__init__, b"replacement")
931+
self.assertEqual(memio.tell(), 2)
932+
self.assertEqual(memio.getvalue(), b"abcd")
933+
self.assertEqual(bytes(view), b"abcd")
934+
memio.write(b"X")
935+
self.assertEqual(memio.getvalue(), b"abXd")
936+
937+
926938
class CStringIOTest(PyStringIOTest):
927939
ioclass = io.StringIO
928940
UnsupportedOperation = io.UnsupportedOperation
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Preserve the position and contents of :class:`io.BytesIO` when
2+
:meth:`!BytesIO.__init__` fails because a buffer is exported.

Modules/_io/bytesio.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1078,15 +1078,16 @@ static int
10781078
_io_BytesIO___init___impl(bytesio *self, PyObject *initvalue)
10791079
/*[clinic end generated code: output=65c0c51e24c5b621 input=3da5a74ee4c4f1ac]*/
10801080
{
1081-
/* In case, __init__ is called multiple times. */
1082-
self->string_size = 0;
1083-
self->pos = 0;
1084-
10851081
if (FT_ATOMIC_LOAD_SSIZE_RELAXED(self->exports) > 0) {
10861082
PyErr_SetString(PyExc_BufferError,
10871083
"Existing exports of data: object cannot be re-sized");
10881084
return -1;
10891085
}
1086+
1087+
/* In case, __init__ is called multiple times. */
1088+
self->string_size = 0;
1089+
self->pos = 0;
1090+
10901091
if (initvalue && initvalue != Py_None) {
10911092
if (PyBytes_CheckExact(initvalue)) {
10921093
Py_XSETREF(self->buf, Py_NewRef(initvalue));

0 commit comments

Comments
 (0)