Skip to content

Commit 4f8a77b

Browse files
[3.14] gh-145105: Fix crash in csv.reader with re-entrant iterator (GH-145106) (#148404)
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 20994b1) Co-authored-by: Ramin Farajpour Cami <ramin.blackhat@gmail.com>
1 parent d6be9fb commit 4f8a77b

File tree

3 files changed

+35
-0
lines changed

3 files changed

+35
-0
lines changed

Lib/test/test_csv.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,33 @@ def test_roundtrip_escaped_unquoted_newlines(self):
553553
self.assertEqual(row, rows[i])
554554

555555

556+
def test_reader_reentrant_iterator(self):
557+
# gh-145105: re-entering the reader from the iterator must not crash.
558+
class ReentrantIter:
559+
def __init__(self):
560+
self.reader = None
561+
self.n = 0
562+
def __iter__(self):
563+
return self
564+
def __next__(self):
565+
self.n += 1
566+
if self.n == 1:
567+
try:
568+
next(self.reader)
569+
except StopIteration:
570+
pass
571+
return "a,b"
572+
if self.n == 2:
573+
return "x"
574+
raise StopIteration
575+
576+
it = ReentrantIter()
577+
reader = csv.reader(it)
578+
it.reader = reader
579+
with self.assertRaises(csv.Error):
580+
next(reader)
581+
582+
556583
class TestDialectRegistry(unittest.TestCase):
557584
def test_registry_badargs(self):
558585
self.assertRaises(TypeError, csv.list_dialects, None)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix crash in :mod:`csv` reader when iterating with a re-entrant iterator
2+
that calls :func:`next` on the same reader from within ``__next__``.

Modules/_csv.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -965,6 +965,12 @@ Reader_iternext_lock_held(PyObject *op)
965965
Py_DECREF(lineobj);
966966
return NULL;
967967
}
968+
if (self->fields == NULL) {
969+
PyErr_SetString(module_state->error_obj,
970+
"iterator has already advanced the reader");
971+
Py_DECREF(lineobj);
972+
return NULL;
973+
}
968974
++self->line_num;
969975
kind = PyUnicode_KIND(lineobj);
970976
data = PyUnicode_DATA(lineobj);

0 commit comments

Comments
 (0)