]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.14] gh-145105: Fix crash in csv.reader with re-entrant iterator (GH-145106) (...
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Sat, 11 Apr 2026 22:46:26 +0000 (00:46 +0200)
committerGitHub <noreply@github.com>
Sat, 11 Apr 2026 22:46:26 +0000 (22:46 +0000)
gh-145105: Fix crash in csv.reader with re-entrant iterator (GH-145106)

When a custom iterator calls next() on the same csv.reader from
within __next__, the inner iteration sets self->fields to NULL.
The outer iteration then crashes in parse_save_field() by passing
NULL to PyList_Append.

Add a guard after PyIter_Next() to detect that fields was set to
NULL by a re-entrant call, and raise csv.Error instead of crashing.
(cherry picked from commit 20994b1809f9c162e4cae01a5af08bd492ede9f9)

Co-authored-by: Ramin Farajpour Cami <ramin.blackhat@gmail.com>
Lib/test/test_csv.py
Misc/NEWS.d/next/Library/2026-02-22-00-00-00.gh-issue-145105.csv-reader-reentrant.rst [new file with mode: 0644]
Modules/_csv.c

index 98ee0c3cdd7a06f4ed619c41caa2595810dac1da..a0a1e91777d89345d543b27ee23049162cf3a0a3 100644 (file)
@@ -553,6 +553,33 @@ class Test_Csv(unittest.TestCase):
                         self.assertEqual(row, rows[i])
 
 
+    def test_reader_reentrant_iterator(self):
+        # gh-145105: re-entering the reader from the iterator must not crash.
+        class ReentrantIter:
+            def __init__(self):
+                self.reader = None
+                self.n = 0
+            def __iter__(self):
+                return self
+            def __next__(self):
+                self.n += 1
+                if self.n == 1:
+                    try:
+                        next(self.reader)
+                    except StopIteration:
+                        pass
+                    return "a,b"
+                if self.n == 2:
+                    return "x"
+                raise StopIteration
+
+        it = ReentrantIter()
+        reader = csv.reader(it)
+        it.reader = reader
+        with self.assertRaises(csv.Error):
+            next(reader)
+
+
 class TestDialectRegistry(unittest.TestCase):
     def test_registry_badargs(self):
         self.assertRaises(TypeError, csv.list_dialects, None)
diff --git a/Misc/NEWS.d/next/Library/2026-02-22-00-00-00.gh-issue-145105.csv-reader-reentrant.rst b/Misc/NEWS.d/next/Library/2026-02-22-00-00-00.gh-issue-145105.csv-reader-reentrant.rst
new file mode 100644 (file)
index 0000000..bc61cc4
--- /dev/null
@@ -0,0 +1,2 @@
+Fix crash in :mod:`csv` reader when iterating with a re-entrant iterator\r
+that calls :func:`next` on the same reader from within ``__next__``.\r
index b994a42775178daab05db1213e11f25289abedd1..fd681f81a3b1972e00958ecfac216f536cbf843b 100644 (file)
@@ -965,6 +965,12 @@ Reader_iternext_lock_held(PyObject *op)
             Py_DECREF(lineobj);
             return NULL;
         }
+        if (self->fields == NULL) {
+            PyErr_SetString(module_state->error_obj,
+                            "iterator has already advanced the reader");
+            Py_DECREF(lineobj);
+            return NULL;
+        }
         ++self->line_num;
         kind = PyUnicode_KIND(lineobj);
         data = PyUnicode_DATA(lineobj);