From 0ebd149eb675887fa8d61292d51eb47f02327252 Mon Sep 17 00:00:00 2001 From: Yongtao Huang Date: Sun, 28 Dec 2025 22:12:04 +0800 Subject: [PATCH 1/8] Fix buffer leak when overlapped operation fails to start (OS-Windows) --- Modules/overlapped.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Modules/overlapped.c b/Modules/overlapped.c index 29b7b356648a53..09b57ce4b9773a 100644 --- a/Modules/overlapped.c +++ b/Modules/overlapped.c @@ -1806,7 +1806,7 @@ _overlapped_Overlapped_WSASendTo_impl(OverlappedObject *self, HANDLE handle, case ERROR_IO_PENDING: Py_RETURN_NONE; default: - self->type = TYPE_NOT_STARTED; + Overlapped_clear(self); return SetFromWindowsErr(err); } } @@ -1873,7 +1873,7 @@ _overlapped_Overlapped_WSARecvFrom_impl(OverlappedObject *self, case ERROR_IO_PENDING: Py_RETURN_NONE; default: - self->type = TYPE_NOT_STARTED; + Overlapped_clear(self); return SetFromWindowsErr(err); } } @@ -1940,7 +1940,7 @@ _overlapped_Overlapped_WSARecvFromInto_impl(OverlappedObject *self, case ERROR_IO_PENDING: Py_RETURN_NONE; default: - self->type = TYPE_NOT_STARTED; + Overlapped_clear(self); return SetFromWindowsErr(err); } } From c523c11fd12dc456151a8446ba3f2a35e64c26aa Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Sun, 28 Dec 2025 14:41:03 +0000 Subject: [PATCH 2/8] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Library/2025-12-28-14-41-02.gh-issue-143249.K4vEp4.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2025-12-28-14-41-02.gh-issue-143249.K4vEp4.rst diff --git a/Misc/NEWS.d/next/Library/2025-12-28-14-41-02.gh-issue-143249.K4vEp4.rst b/Misc/NEWS.d/next/Library/2025-12-28-14-41-02.gh-issue-143249.K4vEp4.rst new file mode 100644 index 00000000000000..0afb560e41957a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-12-28-14-41-02.gh-issue-143249.K4vEp4.rst @@ -0,0 +1 @@ +Fix possible buffer leaks in Windows overlapped I/O operations when the operation fails. From 2508cfb4c50eb7e026c98287f12a9b96203bb0bc Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Sun, 28 Dec 2025 14:41:04 +0000 Subject: [PATCH 3/8] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Library/2025-12-28-14-41-04.gh-issue-143249.K4vEp4.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2025-12-28-14-41-04.gh-issue-143249.K4vEp4.rst diff --git a/Misc/NEWS.d/next/Library/2025-12-28-14-41-04.gh-issue-143249.K4vEp4.rst b/Misc/NEWS.d/next/Library/2025-12-28-14-41-04.gh-issue-143249.K4vEp4.rst new file mode 100644 index 00000000000000..0afb560e41957a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-12-28-14-41-04.gh-issue-143249.K4vEp4.rst @@ -0,0 +1 @@ +Fix possible buffer leaks in Windows overlapped I/O operations when the operation fails. From b1a0ace4cc916f2f7471db6358efa48754c826c3 Mon Sep 17 00:00:00 2001 From: Yongtao Huang Date: Sun, 28 Dec 2025 22:44:28 +0800 Subject: [PATCH 4/8] =?UTF-8?q?Revert=20"=F0=9F=93=9C=F0=9F=A4=96=20Added?= =?UTF-8?q?=20by=20blurb=5Fit."?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 2508cfb4c50eb7e026c98287f12a9b96203bb0bc. --- .../next/Library/2025-12-28-14-41-04.gh-issue-143249.K4vEp4.rst | 1 - 1 file changed, 1 deletion(-) delete mode 100644 Misc/NEWS.d/next/Library/2025-12-28-14-41-04.gh-issue-143249.K4vEp4.rst diff --git a/Misc/NEWS.d/next/Library/2025-12-28-14-41-04.gh-issue-143249.K4vEp4.rst b/Misc/NEWS.d/next/Library/2025-12-28-14-41-04.gh-issue-143249.K4vEp4.rst deleted file mode 100644 index 0afb560e41957a..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-12-28-14-41-04.gh-issue-143249.K4vEp4.rst +++ /dev/null @@ -1 +0,0 @@ -Fix possible buffer leaks in Windows overlapped I/O operations when the operation fails. From 2ac1a80ef26dd5ea2be5e2359de178b5ac8865e9 Mon Sep 17 00:00:00 2001 From: Yongtao Huang Date: Sun, 28 Dec 2025 23:57:32 +0800 Subject: [PATCH 5/8] Add test case --- Lib/test/test_asyncio/test_windows_utils.py | 40 +++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/Lib/test/test_asyncio/test_windows_utils.py b/Lib/test/test_asyncio/test_windows_utils.py index 97f078ff911b5a..b85a0fbc349a2c 100644 --- a/Lib/test/test_asyncio/test_windows_utils.py +++ b/Lib/test/test_asyncio/test_windows_utils.py @@ -129,5 +129,45 @@ def test_popen(self): pass +class OverlappedLeakTests(unittest.TestCase): + def _invalid_socket_handle(self): + return 0 + + def test_overlapped_wsasendto_failure_releases_user_buffer(self): + ov = _overlapped.Overlapped() + buf = bytearray(4096) + with self.assertRaises(OSError): + ov.WSASendTo(self._invalid_socket_handle(), + memoryview(buf), 0, ("127.0.0.1", 1)) + # If the exported buffer is still held, this will raise BufferError. + buf.append(1) + + def test_overlapped_wsarecvfrominto_failure_releases_user_buffer(self): + ov = _overlapped.Overlapped() + buf = bytearray(4096) + with self.assertRaises(OSError): + ov.WSARecvFromInto(self._invalid_socket_handle(), + memoryview(buf), len(buf), 0) + # If the exported buffer is still held, this will raise BufferError. + buf.append(1) + + @support.refcount_test + def test_overlapped_wsarecvfrom_failure_does_not_leak_allocated_buffer(self): + gettotalrefcount = support.get_attribute(sys, "gettotalrefcount") + + def run_once(): + ov = _overlapped.Overlapped() + with self.assertRaises(OSError): + ov.WSARecvFrom(self._invalid_socket_handle(), 4096, 0) + + # Warm up + run_once() + before = gettotalrefcount() + for _ in range(2000): + run_once() + after = gettotalrefcount() + self.assertAlmostEqual(after - before, 0, delta=20) + + if __name__ == '__main__': unittest.main() From 9b5c1c08de08144ac2e5e61123d0c7ada01e84eb Mon Sep 17 00:00:00 2001 From: Yongtao Huang Date: Mon, 29 Dec 2025 00:37:53 +0800 Subject: [PATCH 6/8] Construct a test case suitable for running with -R. --- Lib/test/test_asyncio/test_windows_utils.py | 41 +++++---------------- 1 file changed, 10 insertions(+), 31 deletions(-) diff --git a/Lib/test/test_asyncio/test_windows_utils.py b/Lib/test/test_asyncio/test_windows_utils.py index b85a0fbc349a2c..e2c1254be0f380 100644 --- a/Lib/test/test_asyncio/test_windows_utils.py +++ b/Lib/test/test_asyncio/test_windows_utils.py @@ -129,45 +129,24 @@ def test_popen(self): pass -class OverlappedLeakTests(unittest.TestCase): - def _invalid_socket_handle(self): - return 0 +class OverlappedRefleakTests(unittest.TestCase): - def test_overlapped_wsasendto_failure_releases_user_buffer(self): + def test_wsasendto_failure(self): ov = _overlapped.Overlapped() buf = bytearray(4096) with self.assertRaises(OSError): - ov.WSASendTo(self._invalid_socket_handle(), - memoryview(buf), 0, ("127.0.0.1", 1)) - # If the exported buffer is still held, this will raise BufferError. - buf.append(1) + ov.WSASendTo(0x1234, buf, 0, ("127.0.0.1", 1)) - def test_overlapped_wsarecvfrominto_failure_releases_user_buffer(self): + def test_wsarecvfrom_failure(self): ov = _overlapped.Overlapped() - buf = bytearray(4096) with self.assertRaises(OSError): - ov.WSARecvFromInto(self._invalid_socket_handle(), - memoryview(buf), len(buf), 0) - # If the exported buffer is still held, this will raise BufferError. - buf.append(1) - - @support.refcount_test - def test_overlapped_wsarecvfrom_failure_does_not_leak_allocated_buffer(self): - gettotalrefcount = support.get_attribute(sys, "gettotalrefcount") - - def run_once(): - ov = _overlapped.Overlapped() - with self.assertRaises(OSError): - ov.WSARecvFrom(self._invalid_socket_handle(), 4096, 0) - - # Warm up - run_once() - before = gettotalrefcount() - for _ in range(2000): - run_once() - after = gettotalrefcount() - self.assertAlmostEqual(after - before, 0, delta=20) + ov.WSARecvFrom(0x1234, 1024, 0) + def test_wsarecvfrominto_failure(self): + ov = _overlapped.Overlapped() + buf = bytearray(4096) + with self.assertRaises(OSError): + ov.WSARecvFromInto(0x1234, buf, len(buf), 0) if __name__ == '__main__': unittest.main() From 9ae60174251eaa14e7aba175a3ea1c6019e136e6 Mon Sep 17 00:00:00 2001 From: Yongtao Huang Date: Mon, 29 Dec 2025 00:52:21 +0800 Subject: [PATCH 7/8] Update NEW.s --- .../next/Library/2025-12-28-14-41-02.gh-issue-143249.K4vEp4.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2025-12-28-14-41-02.gh-issue-143249.K4vEp4.rst b/Misc/NEWS.d/next/Library/2025-12-28-14-41-02.gh-issue-143249.K4vEp4.rst index 0afb560e41957a..d50d9e3db850bd 100644 --- a/Misc/NEWS.d/next/Library/2025-12-28-14-41-02.gh-issue-143249.K4vEp4.rst +++ b/Misc/NEWS.d/next/Library/2025-12-28-14-41-02.gh-issue-143249.K4vEp4.rst @@ -1 +1 @@ -Fix possible buffer leaks in Windows overlapped I/O operations when the operation fails. +Fix possible buffer leaks in Windows overlapped I/O on error handling. From d69f8e51c06048d85a5bbb02c1b2d5ff87dc00af Mon Sep 17 00:00:00 2001 From: Yongtao Huang Date: Mon, 29 Dec 2025 05:51:09 +0800 Subject: [PATCH 8/8] Apply suggestion from @picnixz MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Lib/test/test_asyncio/test_windows_utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/test/test_asyncio/test_windows_utils.py b/Lib/test/test_asyncio/test_windows_utils.py index e2c1254be0f380..f9ee2f4f68150a 100644 --- a/Lib/test/test_asyncio/test_windows_utils.py +++ b/Lib/test/test_asyncio/test_windows_utils.py @@ -148,5 +148,6 @@ def test_wsarecvfrominto_failure(self): with self.assertRaises(OSError): ov.WSARecvFromInto(0x1234, buf, len(buf), 0) + if __name__ == '__main__': unittest.main()