From faef7f18a8b7da1716b1b8c130dea592210d39b6 Mon Sep 17 00:00:00 2001 From: VanshAgarwal24036 Date: Mon, 23 Mar 2026 00:05:21 +0530 Subject: [PATCH 1/5] gh-146065: Fix NULL dereference in FutureIter_am_send --- Lib/test/test_asyncio/test_futures.py | 15 +++++++++++++++ ...2026-03-23-00-04-13.gh-issue-146065.FIdn8D.rst | 2 ++ Modules/_asynciomodule.c | 5 +++++ 3 files changed, 22 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2026-03-23-00-04-13.gh-issue-146065.FIdn8D.rst diff --git a/Lib/test/test_asyncio/test_futures.py b/Lib/test/test_asyncio/test_futures.py index 9385a65e52813e..891f83203d5de4 100644 --- a/Lib/test/test_asyncio/test_futures.py +++ b/Lib/test/test_asyncio/test_futures.py @@ -755,6 +755,21 @@ def test_future_disallow_multiple_initialization(self): with self.assertRaises(RuntimeError, msg="is already initialized"): f.__init__(loop=self.loop) + def test_futureiter_send_after_throw_no_crash(self): + async def exploit(): + loop = asyncio.get_event_loop() + fut = loop.create_future() + it = fut.__await__() + it.__next__() + try: + it.throw(RuntimeError) + except RuntimeError: + pass + with self.assertRaises(StopIteration): + it.send(None) + asyncio.run(exploit()) + + @unittest.skipUnless(hasattr(futures, '_CFuture'), 'requires the C _asyncio module') class CFutureTests(BaseFutureTests, test_utils.TestCase): diff --git a/Misc/NEWS.d/next/Library/2026-03-23-00-04-13.gh-issue-146065.FIdn8D.rst b/Misc/NEWS.d/next/Library/2026-03-23-00-04-13.gh-issue-146065.FIdn8D.rst new file mode 100644 index 00000000000000..c30032cd3a16bb --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-03-23-00-04-13.gh-issue-146065.FIdn8D.rst @@ -0,0 +1,2 @@ +Fix a NULL pointer dereference in asyncio.Future iterator when send() is +called after throw() or close(), which previously could cause a crash. diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 8eb8e191530a33..a44adc685b4e05 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -1872,6 +1872,11 @@ FutureIter_am_send(PyObject *op, PyObject **result) { futureiterobject *it = (futureiterobject*)op; + if (it->future == NULL) { + PyErr_SetNone(PyExc_StopIteration); + *result = NULL; + return PYGEN_RETURN; + } /* arg is unused, see the comment on FutureIter_send for clarification */ PySendResult res; Py_BEGIN_CRITICAL_SECTION(it->future); From 45942d68a5b41eeec41a0c67e0764d8fbeea6281 Mon Sep 17 00:00:00 2001 From: VanshAgarwal24036 Date: Mon, 23 Mar 2026 00:12:51 +0530 Subject: [PATCH 2/5] Update --- Modules/_asynciomodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index a44adc685b4e05..aa819ae75c217f 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -1875,7 +1875,7 @@ FutureIter_am_send(PyObject *op, if (it->future == NULL) { PyErr_SetNone(PyExc_StopIteration); *result = NULL; - return PYGEN_RETURN; + return PYGEN_ERROR; } /* arg is unused, see the comment on FutureIter_send for clarification */ PySendResult res; From 8d8051079696da73e6cd0d5227398a039e87a71a Mon Sep 17 00:00:00 2001 From: VanshAgarwal24036 Date: Mon, 23 Mar 2026 00:27:56 +0530 Subject: [PATCH 3/5] Name change --- Lib/test/test_asyncio/test_futures.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_asyncio/test_futures.py b/Lib/test/test_asyncio/test_futures.py index 891f83203d5de4..f2ce778e1dcf6b 100644 --- a/Lib/test/test_asyncio/test_futures.py +++ b/Lib/test/test_asyncio/test_futures.py @@ -756,7 +756,7 @@ def test_future_disallow_multiple_initialization(self): f.__init__(loop=self.loop) def test_futureiter_send_after_throw_no_crash(self): - async def exploit(): + async def run_test(): loop = asyncio.get_event_loop() fut = loop.create_future() it = fut.__await__() @@ -767,7 +767,7 @@ async def exploit(): pass with self.assertRaises(StopIteration): it.send(None) - asyncio.run(exploit()) + asyncio.run(run_test()) @unittest.skipUnless(hasattr(futures, '_CFuture'), From a4109381ba8224fc1e290ca7e11f1f06e39ad5bb Mon Sep 17 00:00:00 2001 From: VanshAgarwal24036 Date: Mon, 23 Mar 2026 00:34:04 +0530 Subject: [PATCH 4/5] Update Test --- Lib/test/test_asyncio/test_futures.py | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/Lib/test/test_asyncio/test_futures.py b/Lib/test/test_asyncio/test_futures.py index f2ce778e1dcf6b..0dfdc3b3a37ae5 100644 --- a/Lib/test/test_asyncio/test_futures.py +++ b/Lib/test/test_asyncio/test_futures.py @@ -756,18 +756,13 @@ def test_future_disallow_multiple_initialization(self): f.__init__(loop=self.loop) def test_futureiter_send_after_throw_no_crash(self): - async def run_test(): - loop = asyncio.get_event_loop() - fut = loop.create_future() - it = fut.__await__() - it.__next__() - try: - it.throw(RuntimeError) - except RuntimeError: - pass - with self.assertRaises(StopIteration): - it.send(None) - asyncio.run(run_test()) + fut = self._new_future() + it = fut.__await__() + next(it) + with self.assertRaises(RuntimeError): + it.throw(RuntimeError) + with self.assertRaises(StopIteration): + it.send(None) @unittest.skipUnless(hasattr(futures, '_CFuture'), From 012819868449d48df37dc1de6406201cc067c7a3 Mon Sep 17 00:00:00 2001 From: VanshAgarwal24036 Date: Mon, 23 Mar 2026 00:43:27 +0530 Subject: [PATCH 5/5] Update Test --- Lib/test/test_asyncio/test_futures.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_asyncio/test_futures.py b/Lib/test/test_asyncio/test_futures.py index 0dfdc3b3a37ae5..d56c2201d90ba2 100644 --- a/Lib/test/test_asyncio/test_futures.py +++ b/Lib/test/test_asyncio/test_futures.py @@ -756,7 +756,7 @@ def test_future_disallow_multiple_initialization(self): f.__init__(loop=self.loop) def test_futureiter_send_after_throw_no_crash(self): - fut = self._new_future() + fut = self._new_future(loop=self.loop) it = fut.__await__() next(it) with self.assertRaises(RuntimeError):