From 85262187a1023fec2e0b14554c2c0bff30ae67ba Mon Sep 17 00:00:00 2001 From: Johannes Gezachew Date: Mon, 14 Sep 2026 21:17:24 +0300 Subject: [PATCH] gh-157379: Fix crash in csv.reader with re-entrant iterator at end of input The gh-145105 guard only runs after the input iterator returns a line. When a re-entrant call leaves a quoted field open and the outer call then reaches the end of input, parse_save_field() appended to the NULL fields list. Raise csv.Error in that branch too. --- Lib/test/test_csv.py | 24 +++++++++++++++++++ ...-09-14-18-15-54.gh-issue-157379.Tq4vRw.rst | 2 ++ Modules/_csv.c | 4 ++++ 3 files changed, 30 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2026-09-14-18-15-54.gh-issue-157379.Tq4vRw.rst diff --git a/Lib/test/test_csv.py b/Lib/test/test_csv.py index 36fa7e3572a5ac2..1925d655ae4fb09 100644 --- a/Lib/test/test_csv.py +++ b/Lib/test/test_csv.py @@ -598,6 +598,30 @@ def __next__(self): with self.assertRaises(csv.Error): next(reader) + def test_reader_reentrant_iterator_eof_in_quoted_field(self): + # gh-157379: the re-entrant call leaves an open quoted field, then + # the outer call reaches the end of input. + 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: + next(self.reader) + raise StopIteration + 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): diff --git a/Misc/NEWS.d/next/Library/2026-09-14-18-15-54.gh-issue-157379.Tq4vRw.rst b/Misc/NEWS.d/next/Library/2026-09-14-18-15-54.gh-issue-157379.Tq4vRw.rst new file mode 100644 index 000000000000000..be20c3f7ba044b9 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-09-14-18-15-54.gh-issue-157379.Tq4vRw.rst @@ -0,0 +1,2 @@ +Fix a crash in :mod:`csv` reader when a re-entrant iterator reaches the end +of input while a quoted field is still open. diff --git a/Modules/_csv.c b/Modules/_csv.c index 6af66c3f09a03b1..8bf32e4d4454f4e 100644 --- a/Modules/_csv.c +++ b/Modules/_csv.c @@ -1029,6 +1029,10 @@ Reader_iternext_lock_held(PyObject *op) if (self->dialect->strict) PyErr_SetString(module_state->error_obj, "unexpected end of data"); + else if (self->fields == NULL) + PyErr_SetString(module_state->error_obj, + "iterator has already advanced " + "the reader"); else if (parse_save_field(self) >= 0) break; }