Skip to content

Commit 8e369d3

Browse files
[3.13] gh-145105: Fix crash in csv.reader with re-entrant iterator (GH-145106) (#148405)
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 a14e4e3 commit 8e369d3

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
@@ -552,6 +552,33 @@ def test_roundtrip_escaped_unquoted_newlines(self):
552552
self.assertEqual(row, rows[i])
553553

554554

555+
def test_reader_reentrant_iterator(self):
556+
# gh-145105: re-entering the reader from the iterator must not crash.
557+
class ReentrantIter:
558+
def __init__(self):
559+
self.reader = None
560+
self.n = 0
561+
def __iter__(self):
562+
return self
563+
def __next__(self):
564+
self.n += 1
565+
if self.n == 1:
566+
try:
567+
next(self.reader)
568+
except StopIteration:
569+
pass
570+
return "a,b"
571+
if self.n == 2:
572+
return "x"
573+
raise StopIteration
574+
575+
it = ReentrantIter()
576+
reader = csv.reader(it)
577+
it.reader = reader
578+
with self.assertRaises(csv.Error):
579+
next(reader)
580+
581+
555582
class TestDialectRegistry(unittest.TestCase):
556583
def test_registry_badargs(self):
557584
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
@@ -957,6 +957,12 @@ Reader_iternext(ReaderObj *self)
957957
Py_DECREF(lineobj);
958958
return NULL;
959959
}
960+
if (self->fields == NULL) {
961+
PyErr_SetString(module_state->error_obj,
962+
"iterator has already advanced the reader");
963+
Py_DECREF(lineobj);
964+
return NULL;
965+
}
960966
++self->line_num;
961967
kind = PyUnicode_KIND(lineobj);
962968
data = PyUnicode_DATA(lineobj);

0 commit comments

Comments
 (0)