From bfad768ef7704f548ef916f6595c1fb678799039 Mon Sep 17 00:00:00 2001 From: Lukas Geiger Date: Wed, 10 Jun 2026 13:27:11 +0100 Subject: [PATCH] gh-150988: Fix refleak in `OSError` when attrs are set before `super().__init__()` (GH-150990) (cherry picked from commit f2a0f82282d6307f7fd2d4ccf52a8fd95ac3922f) Co-authored-by: Lukas Geiger Co-authored-by: Victor Stinner --- Lib/test/test_exceptions.py | 14 ++++++++++++++ .../2026-06-05-22-52-41.gh-issue-150988.fDKfMJ.rst | 2 ++ Objects/exceptions.c | 10 +++++----- 3 files changed, 21 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-06-05-22-52-41.gh-issue-150988.fDKfMJ.rst diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index 04c7e973f5ae700..ca1d2aa86bd0c81 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -1662,6 +1662,20 @@ def inner(): gc_collect() # For PyPy or other GCs. self.assertEqual(wr(), None) + def test_oserror_reinit_leak(self): + # gh-150988: Check for memory leak when re-initializing OSError. + # Previously, setting OSError attributes in a subclass + # before calling super().__init__() leaked memory. + class LeakingOSError(OSError): + def __init__(self, code, message, filename, filename2): + self.strerror = message + self.filename = filename + self.filename2 = filename2 + super().__init__(code, message, filename, None, filename2) + + exc = LeakingOSError(1, "some message", "filename.py", "filename2.py") + exc.__init__(2, "another message", "filename3.py", "filename4.py") + def test_errno_ENOTDIR(self): # Issue #12802: "not a directory" errors are ENOTDIR even on Windows with self.assertRaises(OSError) as cm: diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-06-05-22-52-41.gh-issue-150988.fDKfMJ.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-05-22-52-41.gh-issue-150988.fDKfMJ.rst new file mode 100644 index 000000000000000..6fb70a1ce2685c8 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-05-22-52-41.gh-issue-150988.fDKfMJ.rst @@ -0,0 +1,2 @@ +Fix a reference leak in :exc:`OSError` when attributes are set before +``super().__init__()``. diff --git a/Objects/exceptions.c b/Objects/exceptions.c index 81242e4405d31b5..18c0748df139387 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -1846,10 +1846,10 @@ oserror_init(PyOSErrorObject *self, PyObject **p_args, return -1; } else { - self->filename = Py_NewRef(filename); + Py_XSETREF(self->filename, Py_NewRef(filename)); if (filename2 && filename2 != Py_None) { - self->filename2 = Py_NewRef(filename2); + Py_XSETREF(self->filename2, Py_NewRef(filename2)); } if (nargs >= 2 && nargs <= 5) { @@ -1864,10 +1864,10 @@ oserror_init(PyOSErrorObject *self, PyObject **p_args, } } } - self->myerrno = Py_XNewRef(myerrno); - self->strerror = Py_XNewRef(strerror); + Py_XSETREF(self->myerrno, Py_XNewRef(myerrno)); + Py_XSETREF(self->strerror, Py_XNewRef(strerror)); #ifdef MS_WINDOWS - self->winerror = Py_XNewRef(winerror); + Py_XSETREF(self->winerror, Py_XNewRef(winerror)); #endif /* Steals the reference to args */