https://github.com/python/cpython/commit/1d25b751c5382aa808dbdfd7eacd77cd793418fc
commit: 1d25b751c5382aa808dbdfd7eacd77cd793418fc
branch: main
author: Sachin Shah <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2025-11-05T21:15:27+02:00
summary:
gh-140650: Fix write(), flush() and close() methods of io.BufferedWriter
(GH-140653)
They could raise SystemError or crash when getting the "closed" attribute
or converting it to boolean raises an exception.
files:
A Misc/NEWS.d/next/Library/2025-10-27-00-40-49.gh-issue-140650.DYJPJ9.rst
M Lib/test/test_io/test_bufferedio.py
M Modules/_io/bufferedio.c
diff --git a/Lib/test/test_io/test_bufferedio.py
b/Lib/test/test_io/test_bufferedio.py
index 6e9e96b0e55262..30c34e818b1572 100644
--- a/Lib/test/test_io/test_bufferedio.py
+++ b/Lib/test/test_io/test_bufferedio.py
@@ -962,6 +962,27 @@ def test_args_error(self):
with self.assertRaisesRegex(TypeError, "BufferedWriter"):
self.tp(self.BytesIO(), 1024, 1024, 1024)
+ def test_non_boolean_closed_attr(self):
+ # gh-140650: check TypeError is raised
+ class MockRawIOWithoutClosed(self.MockRawIO):
+ closed = NotImplemented
+
+ bufio = self.tp(MockRawIOWithoutClosed())
+ self.assertRaises(TypeError, bufio.write, b"")
+ self.assertRaises(TypeError, bufio.flush)
+ self.assertRaises(TypeError, bufio.close)
+
+ def test_closed_attr_raises(self):
+ class MockRawIOClosedRaises(self.MockRawIO):
+ @property
+ def closed(self):
+ raise ValueError("test")
+
+ bufio = self.tp(MockRawIOClosedRaises())
+ self.assertRaisesRegex(ValueError, "test", bufio.write, b"")
+ self.assertRaisesRegex(ValueError, "test", bufio.flush)
+ self.assertRaisesRegex(ValueError, "test", bufio.close)
+
class PyBufferedWriterTest(BufferedWriterTest, PyTestCase):
tp = pyio.BufferedWriter
diff --git
a/Misc/NEWS.d/next/Library/2025-10-27-00-40-49.gh-issue-140650.DYJPJ9.rst
b/Misc/NEWS.d/next/Library/2025-10-27-00-40-49.gh-issue-140650.DYJPJ9.rst
new file mode 100644
index 00000000000000..2ae153a64808e8
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-10-27-00-40-49.gh-issue-140650.DYJPJ9.rst
@@ -0,0 +1,3 @@
+Fix an issue where closing :class:`io.BufferedWriter` could crash if
+the closed attribute raised an exception on access or could not be
+converted to a boolean.
diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c
index 0a2b35025321cf..0b4bc4c6b8ad42 100644
--- a/Modules/_io/bufferedio.c
+++ b/Modules/_io/bufferedio.c
@@ -362,16 +362,24 @@ _enter_buffered_busy(buffered *self)
}
#define IS_CLOSED(self) \
- (!self->buffer || \
+ (!self->buffer ? 1 : \
(self->fast_closed_checks \
? _PyFileIO_closed(self->raw) \
: buffered_closed(self)))
#define CHECK_CLOSED(self, error_msg) \
- if (IS_CLOSED(self) && (Py_SAFE_DOWNCAST(READAHEAD(self), Py_off_t,
Py_ssize_t) == 0)) { \
- PyErr_SetString(PyExc_ValueError, error_msg); \
- return NULL; \
- } \
+ do { \
+ int _closed = IS_CLOSED(self); \
+ if (_closed < 0) { \
+ return NULL; \
+ } \
+ if (_closed && \
+ (Py_SAFE_DOWNCAST(READAHEAD(self), Py_off_t, Py_ssize_t) == 0)) \
+ { \
+ PyErr_SetString(PyExc_ValueError, error_msg); \
+ return NULL; \
+ } \
+ } while (0);
#define VALID_READ_BUFFER(self) \
(self->readable && self->read_end != -1)
@@ -2079,6 +2087,7 @@ _io_BufferedWriter_write_impl(buffered *self, Py_buffer
*buffer)
PyObject *res = NULL;
Py_ssize_t written, avail, remaining;
Py_off_t offset;
+ int r;
CHECK_INITIALIZED(self)
@@ -2087,7 +2096,11 @@ _io_BufferedWriter_write_impl(buffered *self, Py_buffer
*buffer)
/* Issue #31976: Check for closed file after acquiring the lock. Another
thread could be holding the lock while closing the file. */
- if (IS_CLOSED(self)) {
+ r = IS_CLOSED(self);
+ if (r < 0) {
+ goto error;
+ }
+ if (r > 0) {
PyErr_SetString(PyExc_ValueError, "write to closed file");
goto error;
}
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]