diff --git a/Lib/test/test_io/test_textio.py b/Lib/test/test_io/test_textio.py index d725f9212ceaae..cbf75277d227d8 100644 --- a/Lib/test/test_io/test_textio.py +++ b/Lib/test/test_io/test_textio.py @@ -1560,6 +1560,23 @@ def closed(self): wrapper = self.TextIOWrapper(raw) wrapper.close() # should not crash + def test_issue143007(self): + # gh-143007: Null pointer dereference in TextIOWrapper.seek + # via re-entrant __int__ + wrapper = self.TextIOWrapper(self.BytesIO(b"x")) + + class Cookie(int): + def __new__(cls, wrapper): + obj = int.__new__(cls, 0) + obj.wrapper = wrapper + return obj + def __int__(self): + self.wrapper.detach() + return 0 + + with self.assertRaises(ValueError): + wrapper.seek(Cookie(wrapper), 0) # should not crash + class PyTextIOWrapperTest(TextIOWrapperTest, PyTestCase): shutdown_error = "LookupError: unknown encoding: ascii" diff --git a/Misc/NEWS.d/next/Library/2025-12-21-21-30-48.gh-issue-143007.EtzUva.rst b/Misc/NEWS.d/next/Library/2025-12-21-21-30-48.gh-issue-143007.EtzUva.rst new file mode 100644 index 00000000000000..3ed76a5043e38c --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-12-21-21-30-48.gh-issue-143007.EtzUva.rst @@ -0,0 +1,2 @@ +Fix crash in :meth:`io.TextIOWrapper.seek` when a custom cookie's +``__int__`` method detaches the underlying buffer. diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c index f9881952561292..2ba8f8e31da814 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -2644,6 +2644,10 @@ _io_TextIOWrapper_seek_impl(textio *self, PyObject *cookieObj, int whence) posobj = PyLong_FromOff_t(cookie.start_pos); if (posobj == NULL) goto fail; + + // gh-143007 PyNumber_Long can call arbitrary code through __int__ which may detach the underlying buffer. + // So we need to re-check that the TextIOWrapper is still attached. + CHECK_ATTACHED(self); res = PyObject_CallMethodOneArg(self->buffer, &_Py_ID(seek), posobj); Py_DECREF(posobj); if (res == NULL)