diff --git a/Lib/test/test_io/test_bufferedio.py b/Lib/test/test_io/test_bufferedio.py index 3278665bdc9dd3..f77cd759ed8229 100644 --- a/Lib/test/test_io/test_bufferedio.py +++ b/Lib/test/test_io/test_bufferedio.py @@ -983,6 +983,17 @@ def closed(self): self.assertRaisesRegex(ValueError, "test", bufio.flush) self.assertRaisesRegex(ValueError, "test", bufio.close) + def test_gh_143375(self): + bufio = self.tp(self.MockRawIO()) + + class EvilIndex: + def __index__(self): + bufio.close() + return 0 + + with self.assertRaisesRegex(ValueError, "seek of closed file"): + bufio.seek(EvilIndex()) + class PyBufferedWriterTest(BufferedWriterTest, PyTestCase): tp = pyio.BufferedWriter diff --git a/Misc/NEWS.d/next/Library/2026-01-08-19-56-40.gh-issue-143375.QCzA_8.rst b/Misc/NEWS.d/next/Library/2026-01-08-19-56-40.gh-issue-143375.QCzA_8.rst new file mode 100644 index 00000000000000..697343c7e8fa6a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-01-08-19-56-40.gh-issue-143375.QCzA_8.rst @@ -0,0 +1,2 @@ +Fix a crash in the ``seek`` method of :class:`~io.BufferedWriter` when +passing an object with a specially crafted :meth:`~object.__index__`. diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c index 4602f2b42a6017..65dfaafc7f43ef 100644 --- a/Modules/_io/bufferedio.c +++ b/Modules/_io/bufferedio.c @@ -1393,6 +1393,10 @@ _io__Buffered_seek_impl(buffered *self, PyObject *targetobj, int whence) if (target == -1 && PyErr_Occurred()) return NULL; + // PyNumber_AsOff_t calls user code via __index__, which + // could have closed the file. + CHECK_CLOSED(self, "seek of closed file") + /* SEEK_SET and SEEK_CUR are special because we could seek inside the buffer. Other whence values must be managed without this optimization. Some Operating Systems can provide additional values, like