https://github.com/python/cpython/commit/fd6bd15c75955fee9cf2103d09de9e3c0f9fea5f
commit: fd6bd15c75955fee9cf2103d09de9e3c0f9fea5f
branch: 3.15
author: Miss Islington (bot) <[email protected]>
committer: encukou <[email protected]>
date: 2026-07-19T17:01:00+02:00
summary:

[3.15] gh-153932: protect read of en_index during enumerate.reduce (GH-154118) 
(GH-154128)

gh-153932: protect read of en_index during enumerate.reduce (GH-154118)
(cherry picked from commit 1604043caf0a00fe4ea9f6e31e389c4cf8b8baab)

Co-authored-by: L. Le <[email protected]>

files:
A 
Misc/NEWS.d/next/Core_and_Builtins/2026-07-19-12-33-27.gh-issue-153932.lKw2bo.rst
M Lib/test/libregrtest/tsan.py
M Lib/test/test_enumerate.py
M Objects/enumobject.c

diff --git a/Lib/test/libregrtest/tsan.py b/Lib/test/libregrtest/tsan.py
index bacfe5e21ba0b7d..c7ec63763bc9d85 100644
--- a/Lib/test/libregrtest/tsan.py
+++ b/Lib/test/libregrtest/tsan.py
@@ -8,6 +8,7 @@
     'test_ctypes',
     'test_concurrent_futures',
     'test_enum',
+    'test_enumerate',
     'test_functools',
     'test_httpservers',
     'test_imaplib',
diff --git a/Lib/test/test_enumerate.py b/Lib/test/test_enumerate.py
index 5cb54cff9b76fdd..c8b85fe86921781 100644
--- a/Lib/test/test_enumerate.py
+++ b/Lib/test/test_enumerate.py
@@ -3,8 +3,11 @@
 import sys
 import pickle
 import gc
+import threading
+
 
 from test import support
+from test.support import threading_helper
 
 class G:
     'Sequence using __getitem__'
@@ -292,5 +295,28 @@ def enum(self, iterable, start=sys.maxsize + 1):
                        (sys.maxsize+3,'c')]
 
 
+@threading_helper.requires_working_threading()
+class TestThreadSafety(EnumerateStartTestCase):
+    def test_thread_safety_while_iterating(self):
+        # gh-153932: calling reduce while iterating should pass with TSAN
+
+        en = enumerate(range(10_000))
+        stop = threading.Event()
+
+        def advance():
+            for _ in en:
+                pass
+            stop.set()
+
+        def read():
+            while not stop.is_set():
+                en.__reduce__()
+
+        threads = [threading.Thread(target=advance), 
threading.Thread(target=read)]
+
+        with threading_helper.start_threads(threads):
+            pass
+
+
 if __name__ == "__main__":
     unittest.main()
diff --git 
a/Misc/NEWS.d/next/Core_and_Builtins/2026-07-19-12-33-27.gh-issue-153932.lKw2bo.rst
 
b/Misc/NEWS.d/next/Core_and_Builtins/2026-07-19-12-33-27.gh-issue-153932.lKw2bo.rst
new file mode 100644
index 000000000000000..56b96307ed04cd9
--- /dev/null
+++ 
b/Misc/NEWS.d/next/Core_and_Builtins/2026-07-19-12-33-27.gh-issue-153932.lKw2bo.rst
@@ -0,0 +1,2 @@
+Fix thread safety issue in the ``__reduce__`` method of
+:py:class:`enumerate`.
diff --git a/Objects/enumobject.c b/Objects/enumobject.c
index fc53f1bfee8dde4..68aa594c5540cee 100644
--- a/Objects/enumobject.c
+++ b/Objects/enumobject.c
@@ -277,10 +277,13 @@ enum_reduce(PyObject *op, PyObject *Py_UNUSED(ignored))
     enumobject *en = _enumobject_CAST(op);
     PyObject *result;
     Py_BEGIN_CRITICAL_SECTION(en);
-    if (en->en_longindex != NULL)
+    if (en->en_longindex != NULL) {
         result = Py_BuildValue("O(OO)", Py_TYPE(en), en->en_sit, 
en->en_longindex);
-    else
-        result = Py_BuildValue("O(On)", Py_TYPE(en), en->en_sit, en->en_index);
+    }
+    else {
+        Py_ssize_t en_index = FT_ATOMIC_LOAD_SSIZE_RELAXED(en->en_index);
+        result = Py_BuildValue("O(On)", Py_TYPE(en), en->en_sit, en_index);
+    }
     Py_END_CRITICAL_SECTION();
     return result;
 }

_______________________________________________
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