Skip to content

_csv.reader: NULL deref via re-entrant iterator #145105

Description

@raminfp

Bug report

Bug description:

A NULL pointer dereference in Modules/_csv.c causes a segfault when a
custom iterator re-enters the same csv.reader from within its __next__
method. The inner iteration sets self->fields to NULL, the outer
iteration then passes that NULL to PyList_Append, crashing the
interpreter.

import _csv


class BadIterator:
    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 = BadIterator()
r = _csv.reader(it)
it.reader = r
next(r)
❯ ./build-asan/python csv_null_deref.py
AddressSanitizer:DEADLYSIGNAL
=================================================================
==293160==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000008 (pc 0x5dc88a0b1c52 bp 0x7ffdd3075180 sp 0x7ffdd3075170 T0)
==293160==The signal is caused by a READ memory access.
==293160==Hint: address points to the zero page.
    #0 0x5dc88a0b1c52 in _Py_TYPE_impl ../Include/object.h:313
    #1 0x5dc88a0b1c52 in PyList_Append ../Objects/listobject.c:541
    #2 0x7ea5a8430ad5 in parse_save_field ../Modules/_csv.c:684
    #3 0x7ea5a843129b in parse_process_char ../Modules/_csv.c:811
    #4 0x7ea5a8431967 in Reader_iternext_lock_held ../Modules/_csv.c:971
    #5 0x7ea5a8431bdf in Reader_iternext ../Modules/_csv.c:993
    #6 0x5dc88a2b0820 in builtin_next ../Python/bltinmodule.c:1756
    #7 0x5dc88a0fb36f in cfunction_vectorcall_FASTCALL ../Objects/methodobject.c:449
    #8 0x5dc88a043ccc in _PyObject_VectorcallTstate ../Include/internal/pycore_call.h:136
    #9 0x5dc88a043dbf in PyObject_Vectorcall ../Objects/call.c:327
    #10 0x5dc88a2c28b7 in _Py_VectorCallInstrumentation_StackRefSteal ../Python/ceval.c:769
    #11 0x5dc88a2d287b in _PyEval_EvalFrameDefault ../Python/generated_cases.c.h:1817
    #12 0x5dc88a309ab0 in _PyEval_EvalFrame ../Include/internal/pycore_ceval.h:118
    #13 0x5dc88a309e16 in _PyEval_Vector ../Python/ceval.c:2132
    #14 0x5dc88a30a0cc in PyEval_EvalCode ../Python/ceval.c:680
    #15 0x5dc88a40d771 in run_eval_code_obj ../Python/pythonrun.c:1366
    #16 0x5dc88a40dab7 in run_mod ../Python/pythonrun.c:1469
    #17 0x5dc88a40e9ec in pyrun_file ../Python/pythonrun.c:1294
    #18 0x5dc88a411822 in _PyRun_SimpleFileObject ../Python/pythonrun.c:518
    #19 0x5dc88a411ace in _PyRun_AnyFileObject ../Python/pythonrun.c:81
    #20 0x5dc88a466df6 in pymain_run_file_obj ../Modules/main.c:410
    #21 0x5dc88a467063 in pymain_run_file ../Modules/main.c:429
    #22 0x5dc88a468861 in pymain_run_python ../Modules/main.c:691
    #23 0x5dc88a468ef7 in Py_RunMain ../Modules/main.c:772
    #24 0x5dc88a4690e3 in pymain_main ../Modules/main.c:802
    #25 0x5dc88a469468 in Py_BytesMain ../Modules/main.c:826
    #26 0x5dc889ece675 in main ../Programs/python.c:15
    #27 0x7ea5a822a3b7 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #28 0x7ea5a822a47a in __libc_start_main_impl ../csu/libc-start.c:360
    #29 0x5dc889ece5a4 in _start (/home/raminfp/Projects/cpython/build-asan/python+0x2ee5a4) (BuildId: 84c80339980a79597ee91e337ac316189316df7c)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV ../Include/object.h:313 in _Py_TYPE_impl
==293160==ABORTING

GDB

gdb -batch \
  -ex "run csv_null_deref.py" \
  -ex "frame 2" -ex "print self->fields" -ex "print self->state" \
  -ex "frame 3" -ex "print c" \
  -ex "frame 1" -ex "print op" \
  ./build-asan/python
Program received signal SIGSEGV, Segmentation fault.
0x0000555555a25c52 in _Py_TYPE_impl (ob=0x0) at ../Include/object.h:313
313	    return ob->ob_type;
#2  0x00007ffff762dad6 in parse_save_field (self=self@entry=0x50c00000c8a0) at ../Modules/_csv.c:684
684	    if (PyList_Append(self->fields, field) < 0) {
$1 = (PyObject *) 0x0
$2 = IN_FIELD
#3  0x00007ffff762e29c in parse_process_char (self=self@entry=0x50c00000c8a0, module_state=module_state@entry=0x507000112680, c=44) at ../Modules/_csv.c:811
811	            if (parse_save_field(self) < 0)
$3 = 44
#1  PyList_Append (op=0x0, newitem=newitem@entry=0x555556527048 <_PyRuntime+125736>) at ../Objects/listobject.c:541
541	    if (PyList_Check(op) && (newitem != NULL)) {
$4 = (PyObject *) 0x0

CPython versions tested on:

CPython main branch

Operating systems tested on:

Linux

Linked PRs

Activity

  1. added a commit that references this issue on Apr 11, 2026
  2. added 2 commits that reference this issue on Apr 11, 2026
  3. added 2 commits that reference this issue on Apr 11, 2026
  4. added a commit that references this issue on Apr 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    extension-modulesC modules in the Modules dirtype-bugAn unexpected behavior, bug, or error

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions