https://github.com/python/cpython/commit/320a73fed3246928361a7c58c3724ff92126d482
commit: 320a73fed3246928361a7c58c3724ff92126d482
branch: 3.15
author: Miss Islington (bot) <[email protected]>
committer: kumaraditya303 <[email protected]>
date: 2026-10-20T11:31:28Z
summary:

[3.15] gh-146065: Fix NULL dereference in FutureIter_am_send (GH-158468) 
(#159870)

gh-146065: Fix NULL dereference in FutureIter_am_send (GH-158468)
(cherry picked from commit 66e391d14ba42218a4bf604973f584853440d6c1)

Co-authored-by: Michael J. Sullivan <[email protected]>

files:
A Misc/NEWS.d/next/Library/2026-03-23-00-04-13.gh-issue-146065.FIdn8D.rst
M Lib/test/test_asyncio/test_futures.py
M Modules/_asynciomodule.c

diff --git a/Lib/test/test_asyncio/test_futures.py 
b/Lib/test/test_asyncio/test_futures.py
index 2a2817e44b26ba..8f53ba5a22013e 100644
--- a/Lib/test/test_asyncio/test_futures.py
+++ b/Lib/test/test_asyncio/test_futures.py
@@ -763,6 +763,24 @@ 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):
+        fut = self._new_future(loop=self.loop)
+        it = fut.__await__()
+        next(it)
+        with self.assertRaises(RuntimeError):
+            it.throw(RuntimeError)
+        with self.assertRaises(StopIteration):
+            it.send(None)
+
+    def test_futureiter_send_after_close_no_crash(self):
+        fut = self._new_future(loop=self.loop)
+        it = fut.__await__()
+        next(it)
+        it.close()
+        with self.assertRaises(StopIteration):
+            it.send(None)
+
+
 @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..db91ce76198c01
--- /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 crash in asyncio.Future iterator when send() is
+called after throw() or close().
diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c
index 249c2e5196aec2..3ffb9b0fb9362d 100644
--- a/Modules/_asynciomodule.c
+++ b/Modules/_asynciomodule.c
@@ -1822,10 +1822,9 @@ FutureIter_dealloc(PyObject *it)
 }
 
 static PySendResult
-FutureIter_am_send_lock_held(futureiterobject *it, PyObject **result)
+FutureIter_am_send_lock_held(FutureObj *fut, PyObject **result)
 {
     PyObject *res;
-    FutureObj *fut = it->future;
     _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(fut);
 
     *result = NULL;
@@ -1856,11 +1855,24 @@ FutureIter_am_send(PyObject *op,
                    PyObject **result)
 {
     futureiterobject *it = (futureiterobject*)op;
+
+    FutureObj *fut;
+    Py_BEGIN_CRITICAL_SECTION(op);
+    fut = (FutureObj*)Py_XNewRef(it->future);
+    Py_END_CRITICAL_SECTION();
+
+    if (fut == NULL) {
+        PyErr_SetNone(PyExc_StopIteration);
+        *result = NULL;
+        return PYGEN_ERROR;
+    }
+
     /* arg is unused, see the comment on FutureIter_send for clarification */
     PySendResult res;
-    Py_BEGIN_CRITICAL_SECTION(it->future);
-    res = FutureIter_am_send_lock_held(it, result);
+    Py_BEGIN_CRITICAL_SECTION(fut);
+    res = FutureIter_am_send_lock_held(fut, result);
     Py_END_CRITICAL_SECTION();
+    Py_DECREF(fut);
     return res;
 }
 
@@ -1954,7 +1966,9 @@ FutureIter_throw(PyObject *op, PyObject *const *args, 
Py_ssize_t nargs)
         goto fail;
     }
 
+    Py_BEGIN_CRITICAL_SECTION(self);
     Py_CLEAR(self->future);
+    Py_END_CRITICAL_SECTION();
 
     PyErr_Restore(type, val, tb);
 
@@ -1967,6 +1981,16 @@ FutureIter_throw(PyObject *op, PyObject *const *args, 
Py_ssize_t nargs)
     return NULL;
 }
 
+static PyObject *
+FutureIter_close(PyObject *self, PyObject *arg)
+{
+    futureiterobject *it = (futureiterobject*)self;
+    Py_BEGIN_CRITICAL_SECTION(self);
+    Py_CLEAR(it->future);
+    Py_END_CRITICAL_SECTION();
+    Py_RETURN_NONE;
+}
+
 static int
 FutureIter_clear(PyObject *op)
 {
@@ -1975,13 +1999,6 @@ FutureIter_clear(PyObject *op)
     return 0;
 }
 
-static PyObject *
-FutureIter_close(PyObject *self, PyObject *arg)
-{
-    (void)FutureIter_clear(self);
-    Py_RETURN_NONE;
-}
-
 static int
 FutureIter_traverse(PyObject *op, visitproc visit, void *arg)
 {

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to