Skip to content

_set_str missing NULL check after PyUnicode_DecodeASCII in _csv.c #146093

Description

@devdanzin

Crash report

What happened?

It's possible to segfault the interpreter on OOM by trying to write a CSV row.

Automated diagnosis:

Bug: On OOM, PyUnicode_DecodeASCII returns NULL, _set_str stores NULL in *target and returns 0 (success). Later PyUnicode_GET_LENGTH(NULL) segfaults.

File: Modules/_csv.c, line 319

MRE:

import _testcapi, csv, io

# Persistent failure ensures the allocation inside _set_str fails
for n in range(1, 30):
    print(n)
    _testcapi.set_nomemory(n, 0)
    try:
        w = csv.writer(io.StringIO())
        w.writerow(["a", "b"])
        _testcapi.remove_mem_hooks()
        break
    except MemoryError:
        _testcapi.remove_mem_hooks()

Backtrace:

Program received signal SIGSEGV, Segmentation fault.
dialect_check_char (name=0x7ffff7fa39e5 "delimiter", c=44, dialect=dialect@entry=0x7ffff74dadb0, allowspace=true) at ./Modules/_csv.c:353
353             PyUnicode_GET_LENGTH(dialect->lineterminator), 1) >= 0)

#0  dialect_check_char (name=0x7ffff7fa39e5 "delimiter", c=44, dialect=dialect@entry=0x7ffff74dadb0, allowspace=true) at ./Modules/_csv.c:353
#1  0x00007ffff7f9ee52 in dialect_new (type=0x555555f539b0, args=0x555555d62c40 <_PyRuntime+135440>, kwargs=0x0) at ./Modules/_csv.c:543
#2  0x000055555576bd44 in type_call (self=0x555555f539b0, args=<optimized out>, kwds=<optimized out>) at Objects/typeobject.c:2441
#3  0x00005555556a34b4 in _PyObject_MakeTpCall (tstate=0x555555d99c08 <_PyRuntime+360664>, callable=0x555555f539b0, args=0x0, nargs=0, keywords=0x0) at Objects/call.c:242
#4  0x00007ffff7fa19d3 in _call_dialect (module_state=0x7ffff72d4cc0, dialect_inst=0x0, kwargs=0x0) at ./Modules/_csv.c:646
#5  csv_writer (module=0x7ffff72b47a0, args=0x7ffff7477cf0, keyword_args=0x0) at ./Modules/_csv.c:1581
#6  0x00005555557210e2 in cfunction_call (func=0x7ffff72cf170, args=0x7ffff7477cf0, kwargs=0x0) at Objects/methodobject.c:564
#7  0x00005555556a34b4 in _PyObject_MakeTpCall (tstate=0x555555d99c08 <_PyRuntime+360664>, callable=0x7ffff72cf170, args=0x7fffffffb628, nargs=1, keywords=0x0) at Objects/call.c:242
#8  0x000055555583f29d in _Py_VectorCall_StackRefSteal (callable=callable@entry=..., arguments=0x7ffff7fa7090, total_args=total_args@entry=1, kwnames=kwnames@entry=...) at Python/ceval.c:733
#9  0x00005555558556d6 in _PyEval_EvalFrameDefault (tstate=tstate@entry=0x555555d99c08 <_PyRuntime+360664>, frame=<optimized out>, frame@entry=0x7ffff7fa7020, throwflag=throwflag@entry=0)
    at Python/generated_cases.c.h:4170
#10 0x000055555583f08b in _PyEval_EvalFrame (tstate=0x555555d99c08 <_PyRuntime+360664>, frame=0x7ffff7fa7020, throwflag=0) at ./Include/internal/pycore_ceval.h:118
#11 _PyEval_Vector (tstate=tstate@entry=0x555555d99c08 <_PyRuntime+360664>, func=func@entry=0x7ffff7466690, locals=locals@entry=0x7ffff746a4b0, args=args@entry=0x0,
    argcount=argcount@entry=0, kwnames=kwnames@entry=0x0) at Python/ceval.c:2130
#12 0x000055555583ee1e in PyEval_EvalCode (co=co@entry=0x555555f396d0, globals=globals@entry=0x7ffff746a4b0, locals=locals@entry=0x7ffff746a4b0) at Python/ceval.c:686
#13 0x00005555559c8f8e in run_eval_code_obj (tstate=0x555555d99c08 <_PyRuntime+360664>, co=co@entry=0x555555f396d0, globals=globals@entry=0x7ffff746a4b0, locals=locals@entry=0x7ffff746a4b0)
    at Python/pythonrun.c:1368
#14 0x00005555559c8adb in run_mod (mod=mod@entry=0x555555f40fa0, filename=filename@entry=0x7ffff74e8580, globals=globals@entry=0x7ffff746a4b0, locals=locals@entry=0x7ffff746a4b0,
    flags=0x7fffffffc950, arena=arena@entry=0x7ffff74dad40, interactive_src=0x0, generate_new_source=0) at Python/pythonrun.c:1471

Found using cpython-review-toolkit with Claude Opus 4.6, using the /cpython-review-toolkit:explore Modules/_csv.c all deep command.

CPython versions tested on:

CPython main branch

Operating systems tested on:

Linux

Output from running 'python -VV' on the command line:

Python 3.15.0a7+ (heads/main:e0f7c1097e1, Mar 17 2026, 18:10:52) [Clang 21.1.2 (2ubuntu6)]

Linked PRs

Activity

  1. added
    type-crashA hard crash of the interpreter, possibly with a core dump
    on Mar 17, 2026
  2. added 3 commits that reference this issue on Mar 18, 2026
  3. vstinner commented on Mar 18, 2026

    @vstinner
    Member

    I wrote #146113 to fix the issue.

  4. added a commit that references this issue on Mar 18, 2026
  5. added 2 commits that reference this issue on Mar 18, 2026
  6. vstinner commented on Mar 18, 2026

    @vstinner
    Member

    Issue fixed by #146113. Thanks for the bug report.

  7. added 2 commits that reference this issue on Mar 18, 2026
  8. smontanaro commented on Mar 19, 2026

    @smontanaro
    Contributor

    I'm certainly no expert on crashers, but would it have been possible to add a unit test for this? Even @devdanzin's report code would seem to have been helpful.

  9. vstinner commented on Mar 19, 2026

    @vstinner
    Member

    Causing PyUnicode_DecodeASCII() to fail with a memory allocation failure requires to tune _testcapi.set_nomemory() exactly, but the set_nomemory() argument can vary over time if Python internals change.

    I don't think that we can write a reliable test with reasonable maintenance.

  10. 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

    3.14bugs and security fixes3.15bugs and security fixesextension-modulesC modules in the Modules dirtype-crashA hard crash of the interpreter, possibly with a core dump

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions