Thanks to visit codestin.com
Credit goes to github.com

Skip to content

_csv.reader: NULL deref via re-entrant iterator that reaches EOF with an open quoted field #157379

Description

@ashm-dev

Bug report

Bug description:

csv.reader() crashes with a NULL pointer dereference in parse_save_field()
when its input iterator re-enters the same reader and the outer call then hits
end of input while state == IN_QUOTED_FIELD.

This is a second, still open path of gh-145105 (the fix in PR #145106 added the
self->fields == NULL guard only after a successful PyIter_Next(), not in the
end-of-input branch).

Reproduction

import csv


class It:
    def __init__(self):
        self.n = 0
        self.reader = None

    def __iter__(self):
        return self

    def __next__(self):
        self.n += 1
        if self.n == 1:
            next(self.reader)  # re-enter the same reader
            raise StopIteration
        if self.n == 2:
            return '"x'  # inner call: opens a quoted field
        raise StopIteration


it = It()
r = csv.reader(it)
it.reader = r
print(next(r))

python repro.py segfaults.

Expected

A csv.Error (the same "iterator has already advanced the reader" raised by
the gh-145105 fix), or at worst a clean StopIteration. No crash.

Actual

Fatal Python error: Segmentation fault
...
  #5 0x... in parse_save_field .../Modules/_csv.c:713
  #6 0x... in Reader_iternext_lock_held .../Modules/_csv.c:978

Affected versions

build result
main (575fe39, pydebug + ASAN + UBSAN) NULL deref in PyList_Append, Fatal Python error: Segmentation fault
3.15.0rc2+dev (e325fae) AddressSanitizer: SEGV on unknown address 0x000000000008 in _Py_TYPE_impl / PyList_Append
3.14.7 Segmentation fault (core dumped), exit 139
3.13.15+ Segmentation fault (core dumped), exit 139

Not a regression in main; the bug is shared by all maintained branches.

Cause

Reader_iternext_lock_held() (Modules/_csv.c). When the input iterator returns
NULL, the end-of-input branch saves a pending field:

lineobj = PyIter_Next(self->input_iter);
if (lineobj == NULL) {
    /* End of input OR exception */
    if (!PyErr_Occurred() && (self->field_len != 0 ||
                              self->state == IN_QUOTED_FIELD)) {
        if (self->dialect->strict)
            PyErr_SetString(module_state->error_obj,
                            "unexpected end of data");
        else if (parse_save_field(self) >= 0)   /* <- _csv.c:978 */
            break;
    }
    return NULL;
}
if (!PyUnicode_Check(lineobj)) { ... }
if (self->fields == NULL) {                 /* <- gh-145105 guard, too late */
    PyErr_SetString(module_state->error_obj,
                    "iterator has already advanced the reader");
    ...
}

A re-entrant next(reader) inside the input iterator completes successfully and
sets self->fields = NULL, but leaves self->state == IN_QUOTED_FIELD (the row
was returned through the end-of-input break path without resetting the state).
Control returns to the outer call, whose PyIter_Next() now gets
StopIteration. The outer call enters the lineobj == NULL branch and calls
parse_save_field(), which does PyList_Append(self->fields, field) with
self->fields == NULL.

Workaround

csv.reader(..., strict=True) raises _csv.Error: unexpected end of data
instead of crashing, because the strict branch returns before
parse_save_field().

Related

Build used for the sanitizer output

CC=clang CXX=clang++ LDFLAGS='-fuse-ld=lld' ./configure \
    --with-pydebug --with-address-sanitizer --with-undefined-behavior-sanitizer

Run as ASAN_OPTIONS=detect_leaks=0 ./python -X dev repro.py.

Full output on main (UBSan + ASan)
Include/object.h:234:16: runtime error: member access within null pointer of type 'PyObject' (aka 'struct _object')
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior Include/object.h:234:16 
Fatal Python error: Segmentation fault

Current thread 0x00007fc6676fd400 [python] (most recent call first):
  File "/tmp/csvbug/repro.py", line 25 in <module>

Current thread's C stack trace (most recent call first):
  Binary file "./python", at +0xde4bdd [0x557ff6a8ebdd]
  Binary file "./python", at _Py_DumpStack+0x10c [0x557ff78c5f24]
  Binary file "./python", at +0x1c97ee7 [0x557ff7941ee7]
  Binary file "/usr/lib/libc.so.6", at +0x3e6f0 [0x7fc66743e6f0]
  Binary file "./python", at PyList_Append+0x51 [0x557ff708d969]
  Binary file "/home/boss/projects/oss/cpython/main/build/lib.linux-x86_64-3.16/_csv.cpython-316d-x86_64-linux-gnu.so", at +0x1ab06 [0x7fc66695eb06]
  Binary file "/home/boss/projects/oss/cpython/main/build/lib.linux-x86_64-3.16/_csv.cpython-316d-x86_64-linux-gnu.so", at +0x19bf2 [0x7fc66695dbf2]
  Binary file "./python", at +0x183c5ef [0x557ff74e65ef]
  Binary file "./python", at +0x14ac791 [0x557ff7156791]
  Binary file "./python", at +0x12f6fd6 [0x557ff6fa0fd6]
  Binary file "./python", at _Py_VectorCallInstrumentation_StackRefSteal+0x179 [0x557ff74f3be1]
  Binary file "./python", at _PyEval_EvalFrameDefault+0x5239a [0x557ff7549012]
  Binary file "./python", at PyEval_EvalCode+0x2d9 [0x557ff74f2931]
  Binary file "./python", at +0x1b9b43b [0x557ff784543b]
  Binary file "./python", at +0x1b9ab62 [0x557ff7844b62]
  Binary file "./python", at +0x1b95670 [0x557ff783f670]
  Binary file "./python", at +0x1b938b5 [0x557ff783d8b5]
  Binary file "./python", at +0x1b92e8d [0x557ff783ce8d]
  Binary file "./python", at +0x1c5cc10 [0x557ff7906c10]
  Binary file "./python", at Py_RunMain+0xe48 [0x557ff7905300]
  Binary file "./python", at +0x1c5c350 [0x557ff7906350]
  Binary file "./python", at Py_BytesMain+0xd5 [0x557ff79065dd]
  Binary file "/usr/lib/libc.so.6", at +0x27781 [0x7fc667427781]
  Binary file "/usr/lib/libc.so.6", at __libc_start_main+0x89 [0x7fc6674278b9]
  Binary file "./python", at _start+0x25 [0x557ff6a26a65]
AddressSanitizer:DEADLYSIGNAL
=================================================================
==637567==ERROR: AddressSanitizer: SEGV on unknown address 0x03e80009ba7f (pc 0x7fc66749a17c bp 0x7e56667f2c50 sp 0x7e56667f2c20 T0)
==637567==The signal is caused by a READ memory access.
    #0 0x7fc66749a17c  (/usr/lib/libc.so.6+0x9a17c) (BuildId: 503200d7fda94a5dc6058d7e0694e5d1dcb2e372)
    #1 0x7fc66743e5cf in raise (/usr/lib/libc.so.6+0x3e5cf) (BuildId: 503200d7fda94a5dc6058d7e0694e5d1dcb2e372)
    #2 0x7fc66743e6ef  (/usr/lib/libc.so.6+0x3e6ef) (BuildId: 503200d7fda94a5dc6058d7e0694e5d1dcb2e372)
    #3 0x557ff708d968 in _Py_TYPE_impl /home/boss/projects/oss/cpython/main/./Include/object.h:234:16
    #4 0x557ff708d968 in PyList_Append /home/boss/projects/oss/cpython/main/Objects/listobject.c:541:9
    #5 0x7fc66695eb05 in parse_save_field /home/boss/projects/oss/cpython/main/./Modules/_csv.c:713:9
    #6 0x7fc66695dbf1 in Reader_iternext_lock_held /home/boss/projects/oss/cpython/main/./Modules/_csv.c:978:26
    #7 0x7fc66695dbf1 in Reader_iternext /home/boss/projects/oss/cpython/main/./Modules/_csv.c:1028:14
    #8 0x557ff74e65ee in builtin_next /home/boss/projects/oss/cpython/main/Python/bltinmodule.c:1777:11
    #9 0x557ff7156790 in cfunction_vectorcall_FASTCALL /home/boss/projects/oss/cpython/main/Objects/methodobject.c:449:24
    #10 0x557ff6fa0fd5 in _PyObject_VectorcallTstate /home/boss/projects/oss/cpython/main/./Include/internal/pycore_call.h:144:11
    #11 0x557ff74f3be0 in _Py_VectorCallInstrumentation_StackRefSteal /home/boss/projects/oss/cpython/main/Python/ceval.c:770:11
    #12 0x557ff7549011 in _PyEval_EvalFrameDefault /home/boss/projects/oss/cpython/main/Python/generated_cases.c.h:1906:35
    #13 0x557ff74f2930 in _PyEval_Vector /home/boss/projects/oss/cpython/main/Python/ceval.c:2176:12
    #14 0x557ff74f2930 in PyEval_EvalCode /home/boss/projects/oss/cpython/main/Python/ceval.c:681:21
    #15 0x557ff784543a in run_eval_code_obj /home/boss/projects/oss/cpython/main/Python/pythonrun.c:1406:12
    #16 0x557ff7844b61 in run_mod /home/boss/projects/oss/cpython/main/Python/pythonrun.c:1509:19
    #17 0x557ff783f66f in _PyRun_File /home/boss/projects/oss/cpython/main/Python/pythonrun.c:1332:15
    #18 0x557ff783d8b4 in _PyRun_SimpleFile /home/boss/projects/oss/cpython/main/Python/pythonrun.c:544:15
    #19 0x557ff783ce8c in _PyRun_AnyFile /home/boss/projects/oss/cpython/main/Python/pythonrun.c:92:18
    #20 0x557ff7906c0f in pymain_run_file_obj /home/boss/projects/oss/cpython/main/Modules/main.c:478:24
    #21 0x557ff7906c0f in pymain_run_file /home/boss/projects/oss/cpython/main/Modules/main.c:494:20
    #22 0x557ff79052ff in pymain_run_python /home/boss/projects/oss/cpython/main/Modules/main.c:804:21
    #23 0x557ff79052ff in Py_RunMain /home/boss/projects/oss/cpython/main/Modules/main.c:891:5
    #24 0x557ff790634f in pymain_main /home/boss/projects/oss/cpython/main/Modules/main.c:921:12
    #25 0x557ff79065dc in Py_BytesMain /home/boss/projects/oss/cpython/main/Modules/main.c:945:12
    #26 0x7fc667427780  (/usr/lib/libc.so.6+0x27780) (BuildId: 503200d7fda94a5dc6058d7e0694e5d1dcb2e372)
    #27 0x7fc6674278b8 in __libc_start_main (/usr/lib/libc.so.6+0x278b8) (BuildId: 503200d7fda94a5dc6058d7e0694e5d1dcb2e372)
    #28 0x557ff6a26a64 in _start (/home/boss/projects/oss/cpython/main/python+0xd7ca64) (BuildId: f312134e13fb614412b5e6b12b00d0f08bdbd276)

==637567==Register values:
rax = 0x0000000000000000  rbx = 0x000000000009ba7f  rcx = 0x00007fc66749a17c  rdx = 0x000000000000000b  
rdi = 0x000000000009ba7f  rsi = 0x000000000009ba7f  rbp = 0x00007e56667f2c50  rsp = 0x00007e56667f2c20  
 r8 = 0x0000557ff82bd528   r9 = 0x0000000000000038  r10 = 0x0000000000000000  r11 = 0x0000000000000246  
r12 = 0x00007c86668042e0  r13 = 0x00000000ffffffff  r14 = 0x000000000000000b  r15 = 0x0000000000000008  
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV (/usr/lib/libc.so.6+0x9a17c) (BuildId: 503200d7fda94a5dc6058d7e0694e5d1dcb2e372) 
==637567==ABORTING
Full output on 3.15 (UBSan + ASan)
Include/object.h:234:16: runtime error: member access within null pointer of type 'PyObject' (aka 'struct _object')
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior Include/object.h:234:16 
AddressSanitizer:DEADLYSIGNAL
=================================================================
==637570==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000008 (pc 0x55e3b4990149 bp 0x7ffedac5fbe0 sp 0x7ffedac5fbc0 T0)
==637570==The signal is caused by a READ memory access.
==637570==Hint: address points to the zero page.
    #0 0x55e3b4990149 in _Py_TYPE_impl /home/boss/projects/oss/cpython/3.15/./Include/object.h:234:16
    #1 0x55e3b4990149 in PyList_Append /home/boss/projects/oss/cpython/3.15/Objects/listobject.c:541:9
    #2 0x7f67751ea725 in parse_save_field /home/boss/projects/oss/cpython/3.15/./Modules/_csv.c:688:9
    #3 0x7f67751e9811 in Reader_iternext_lock_held /home/boss/projects/oss/cpython/3.15/./Modules/_csv.c:953:26
    #4 0x7f67751e9811 in Reader_iternext /home/boss/projects/oss/cpython/3.15/./Modules/_csv.c:1003:14
    #5 0x55e3b4de936e in builtin_next /home/boss/projects/oss/cpython/3.15/Python/bltinmodule.c:1776:11
    #6 0x55e3b4a5759f in cfunction_vectorcall_FASTCALL /home/boss/projects/oss/cpython/3.15/Objects/methodobject.c:449:24
    #7 0x55e3b48a8415 in _PyObject_VectorcallTstate /home/boss/projects/oss/cpython/3.15/./Include/internal/pycore_call.h:144:11
    #8 0x55e3b4df6ad0 in _Py_VectorCallInstrumentation_StackRefSteal /home/boss/projects/oss/cpython/3.15/Python/ceval.c:777:11
    #9 0x55e3b4e3a16b in _PyEval_EvalFrameDefault /home/boss/projects/oss/cpython/3.15/Python/generated_cases.c.h:1846:35
    #10 0x55e3b4df5820 in _PyEval_Vector /home/boss/projects/oss/cpython/3.15/Python/ceval.c:2153:12
    #11 0x55e3b4df5820 in PyEval_EvalCode /home/boss/projects/oss/cpython/3.15/Python/ceval.c:688:21
    #12 0x55e3b51110ca in run_eval_code_obj /home/boss/projects/oss/cpython/3.15/Python/pythonrun.c:1398:12
    #13 0x55e3b51107f1 in run_mod /home/boss/projects/oss/cpython/3.15/Python/pythonrun.c:1501:19
    #14 0x55e3b510b25f in _PyRun_File /home/boss/projects/oss/cpython/3.15/Python/pythonrun.c:1324:15
    #15 0x55e3b510931e in _PyRun_SimpleFile /home/boss/projects/oss/cpython/3.15/Python/pythonrun.c:536:15
    #16 0x55e3b51088fc in _PyRun_AnyFile /home/boss/projects/oss/cpython/3.15/Python/pythonrun.c:84:18
    #17 0x55e3b51d0779 in pymain_run_file_obj /home/boss/projects/oss/cpython/3.15/Modules/main.c:437:24
    #18 0x55e3b51d0779 in pymain_run_file /home/boss/projects/oss/cpython/3.15/Modules/main.c:458:20
    #19 0x55e3b51ce8ad in pymain_run_python /home/boss/projects/oss/cpython/3.15/Modules/main.c:750:21
    #20 0x55e3b51ce8ad in Py_RunMain /home/boss/projects/oss/cpython/3.15/Modules/main.c:837:5
    #21 0x55e3b51cf97f in pymain_main /home/boss/projects/oss/cpython/3.15/Modules/main.c:867:12
    #22 0x55e3b51cfc0c in Py_BytesMain /home/boss/projects/oss/cpython/3.15/Modules/main.c:891:12
    #23 0x7f6775c27780  (/usr/lib/libc.so.6+0x27780) (BuildId: 503200d7fda94a5dc6058d7e0694e5d1dcb2e372)
    #24 0x7f6775c278b8 in __libc_start_main (/usr/lib/libc.so.6+0x278b8) (BuildId: 503200d7fda94a5dc6058d7e0694e5d1dcb2e372)
    #25 0x55e3b433ce64 in _start (/home/boss/projects/oss/cpython/3.15/python+0xd79e64) (BuildId: e127fbc7ed2e5451fe39515b06306c80828a344f)

==637570==Register values:
rax = 0x0000000000000001  rbx = 0x0000000000000000  rcx = 0x00007ffedac5f868  rdx = 0x0000000000000000  
rdi = 0x00007ffedac5f800  rsi = 0x0000000000000000  rbp = 0x00007ffedac5fbe0  rsp = 0x00007ffedac5fbc0  
 r8 = 0x00007f6775200760   r9 = 0x000055e3b5c06a40  r10 = 0x000055e3b5bf9200  r11 = 0x0000000000000202  
r12 = 0x00007c27750001a0  r13 = 0x00000000ffffffff  r14 = 0x000055e3b5b89c30  r15 = 0x0000000000000008  
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /home/boss/projects/oss/cpython/3.15/./Include/object.h:234:16 in _Py_TYPE_impl
==637570==ABORTING
3.13 / 3.14 output
$ /home/boss/.local/bin/python3.13 repro.py
Segmentation fault (core dumped)   # exit 139
$ python3 repro.py
Segmentation fault (core dumped)   # exit 139

CPython versions tested on:

CPython main branch, 3.16, 3.15, 3.14, 3.13

Operating systems tested on:

Linux

Activity

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