GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — CPython crashes on memory-allocation failure (OOM): 35 findings (4 dups → 31 distinct) · Issue #151763 · python/cpython

CPython crashes on memory-allocation failure (OOM): 35 findings (4 dups → 31 distinct) #151763

New issue
Copy link
New issue
Copy link
Open
10 / 1510 of 15 issues completed
Open
CPython crashes on memory-allocation failure (OOM): 35 findings (4 dups → 31 distinct)#151763
10 / 1510 of 15 issues completed
Copy link
Labels
extension-modulesC modules in the Modules dirC modules in the Modules dirinterpreter-core(Objects, Python, Grammar, and Parser dirs)(Objects, Python, Grammar, and Parser dirs)type-crashA hard crash of the interpreter, possibly with a core dumpA hard crash of the interpreter, possibly with a core dump

Description

@devdanzin
devdanzin
opened on Jun 20, 2026
Issue body actions

Crash report

What happened?

Allocation-failure fuzzing of CPython main turned up 35 distinct ways the interpreter
crashes
— segfault, failed assert(...), or Py_FatalError — when a memory allocation (4 have since been confirmed as duplicates via rr → 31 distinct; see the dedup comment)
fails part-way through an operation. Each is a separate underlying bug with a minimal,
stdlib-only reproducer, a backtrace, a root-cause analysis, and a suggested fix, published
as a self-contained gist (linked below).

I'm filing them under one umbrella so they can be picked off individually without flooding
the tracker with 35 issues at once. To take one: open a normal CPython issue (or PR) for
it and drop a comment here with the link — I'll mark it in the table. If any turn out to be
duplicates or non-bugs, say so and I'll annotate them.

These were found with fusil's OOM-injection mode
(fusil originally by @vstinner). The reports and reduced reproducers were drafted with
AI assistance (Claude Code) and then reviewed and re-verified by hand — see Disclosure.

Reproducing

  • Found on main (3.16.0a0) at commit 15d7406. These are allocation-path bugs, so they
    are not tied to that exact revision.
  • Every gist ships a minimal OOM-ID-repro.py (standard library only) that drives the failing path
    while _testcapi.set_nomemory(...) forces allocations to fail. Just run python OOM-ID-repro.py — where the exact failing-allocation index is sensitive to the build's
    allocation count, the repro self-sweeps (a fresh subprocess per index) and stops at the
    first crash, so no manual tuning is needed.
  • _testcapi.set_nomemory requires a test/debug interpreter (--with-pydebug exposes it).
  • Build matrix (in each report): many of these are debug-only assertion / Py_FatalError
    failures — the assert(...) is compiled out under -DNDEBUG, so a release build doesn't
    abort. They are still real bugs: on release the same defect is latent undefined behaviour
    (a use-after-free, a Py_DECREF(NULL), or a silently-lost exception). A subset segfault on
    release builds directly; those are called out below.

Highest-confidence starting points

These crash a release build (not just a debug assertion) and have a clean minimal
reproducer — the lowest-effort to confirm and fix:

  • OOM-0034, OOM-0028 — unchecked PyUnicode_AsUTF8 / PyUnicode_EncodeFSDefault
    returning NULL is dereferenced (≈ one-line NULL checks).
  • OOM-0001, OOM-0002, OOM-0014 — Py_DECREF(NULL) on an unchecked-allocation
    error path.
  • OOM-0031 — _interpreters.capture_exception calls _PyXI_FreeExcInfo(NULL) with no
    NULL guard.
  • OOM-0012, OOM-0020 — instrumentation / thread-state corruption under OOM.

Findings

Status: blank = not yet filed · #N = open issue · #N (PR) = open pull request · #N FIXED = filed and fixed (issue closed-completed or PR merged) · 🔁 = confirmed duplicate, folded into another finding — please don't pursue.

Segfaults (7)

Report Title Status
OOM-0001 Segfault: Py_DECREF of a NULL filename in do_warn (_warnings.c:1139) #151673
OOM-0002 Segfault: Py_DECREF(NULL) in PyContextVar_Set (context.c:367) #151773 FIXED
OOM-0024 Segfault: dealloc of uninitialized iterator in template_iter (templateobject.c:232) #151815
OOM-0028 Segfault: NULL deref in os__path_normpath_impl (posixmodule.c:6149) #152058 FIXED
OOM-0031 Segfault: NULL info deref in _excinfo_clear_type (crossinterp.c:1319) #151842 FIXED
OOM-0033 [SUPERSEDED — duplicate of OOM-0036 / #151818] Segfault / negative-refcount: over-decreffed sys.path entry in PyType_IsSubtype (typeobject.c:2931) 🔁 dup of OOM-0036 → #151818
OOM-0034 Segfault: unchecked PyUnicode_AsUTF8 NULL deref in pegen.c:33 #151798 (PR)

Assertion failures / aborts (23)

Report Title Status
OOM-0003 Abort: uninitialized _co_unique_id assert in code_dealloc (codeobject.c:2440) #152034 FIXED (PR)
OOM-0004 Abort/Segfault: corrupted object freelist in clear_freelist (object.c:909) #152125
OOM-0005 [SUPERSEDED — duplicate of OOM-0036 / #151818] Abort: negative-refcount over-decref in _PyFrame_ClearLocals (frame.c:101) 🔁 dup of OOM-0036 → #151818 (reported as #152147, closed as dup)
OOM-0006 Abort/Segfault: _PyObject_GC_UNTRACK assert on untracked iterator in dictiter_dealloc (dictobject.c:5532) #152107
OOM-0008 Abort: assert(!PyErr_Occurred()) in _PyType_LookupStackRefAndVersion (typeobject.c:6343) #151905 FIXED
OOM-0009 Abort: stale release1 flag trips an ownership assert in replace (unicodeobject.c:10783) #152228
OOM-0010 Abort: assert(_PyErr_Occurred(tstate)) in _PyEval_EvalFrameDefault (generated_cases.c.h:13817)
OOM-0011 [SUPERSEDED — duplicate of OOM-0008] Abort: assert(!PyErr_Occurred()) in specialize (specialize.c:364) 🔁 dup of OOM-0008 (above)
OOM-0012 Abort/Segfault: stale instrumentation in get_tools_for_instruction (instrumentation.c:1106)
OOM-0013 Abort: builtin breaks result/error contract in _Py_BuiltinCallFastWithKeywords_StackRef (ceval.c:843) #151968 FIXED (PR)
OOM-0014 Abort/Segfault: unchecked NULL in channelsmod__channel_id (_interpchannelsmodule.c:3487) #151902 (PR)
OOM-0015 Abort: stale exception in cfunction_check_kwargs (methodobject.c:409)
OOM-0016 Abort: assert(!queue->alive) in _queue_clear (_interpqueuesmodule.c:559) #152130
OOM-0017 Abort: negative gc_refs ("refcount too small") in validate_gc_objects (gc_free_threading.c:1116)
OOM-0018 Abort: ownership assert in set_keys (dictobject.c:205) #152254
OOM-0019 Abort: double-free in _PyPegen_raise_error_known_location (pegen_errors.c:363) #151931 (PR)
OOM-0025 Abort: assert(!PyErr_Occurred()) in unspecialize (specialize.c:378)
OOM-0026 Abort: err-code vs PyErr desync in handle_channel_error (_interpchannelsmodule.c:398 / :443) #152156 FIXED
OOM-0027 Abort: assert(PyStackRef_BoolCheck(cond)) in POP_JUMP_IF_FALSE (generated_cases.c.h:11120)
OOM-0029 [SUPERSEDED — duplicate of OOM-0036 / #151818] Abort: negative refcount on a MemoryError (tuple_dealloc, tupleobject.c:277) 🔁 dup of OOM-0036 → #151818
OOM-0030 Abort: Py_DECREF of NULL-data unicode in unicode_subtype_new (unicodeobject.c:13986)
OOM-0032 Abort: pending-exception assert from warn_explicit normalization (_warnings.c:799/806)
OOM-0035 Abort / malformed str: invalid maxchar in _PyUnicode_FromUCS4 (unicodeobject.c:2228)

Fatal Python error (5)

Report Title Status
OOM-0007 Fatal: context_tp_dealloc clears the pending exception (context.c:535) #152083
OOM-0020 Fatal: _PyMem_DebugRawFree: bad ID in free_threadstate (pystate.c:1527)
OOM-0021 Fatal: NULL returned without an exception set in _Py_CheckFunctionResult (call.c:43)
OOM-0022 Fatal: stale MemoryError trips _Py_CheckSlotResult in reload_singlephase_extension (import.c:2011)
OOM-0023 Fatal: dealloc clears the in-flight exception in subtype_dealloc (typeobject.c:2719)

Related groups (one fix may cover several)

  • Dealloc clears the in-flight exception (the tp_dealloc docs should mention error indicator may be set #89373 _Py_Dealloc invariant): OOM-0007
    (context_tp_dealloc) and OOM-0023 (the generic subtype_dealloc, covering a family of
    pure-Python types) free an object while a MemoryError is pending and don't save/restore it.
  • A pending exception survives into code that asserts there is none (specializer / eval /
    call layers): OOM-0008, 0010, 0015, 0025, 0032 (OOM-0011 was a second detector face of
    OOM-0008 — same frame.f_back swallow — and is folded). Several share the theme that the
    adaptive specializer / call machinery is entered with a MemoryError already pending.
  • Py_DECREF/Py_CLEAR of a NULL or partially-initialized object on the OOM error path:
    OOM-0001, 0002, 0006, 0014, 0024, 0030, 0031 (an allocation fails after a slot is taken
    but before the object is valid, and the error path frees it anyway).
  • Over-decref → negative refcount under OOM (a real memory-safety bug; the assert is the
    debug-build detector): OOM-0019. (rr later showed OOM-0005, OOM-0029 and OOM-0033 are
    all the same _CALL_LIST_APPEND list.append()-under-MemoryError double-free —
    OOM-0036, filed #151818 — and are
    folded; OOM-0019 is a distinct pegen double-free.)
  • Free-threading-specific: OOM-0003 (_co_unique_id), OOM-0017 (cyclic GC),
    OOM-0018 (managed dict), OOM-0020 (thread-state reservation).

Disclosure & caveats

  • The reports and reduced reproducers were drafted with AI assistance (Claude Code); each
    gist carries an explicit disclaimer. The reproducers were re-run on the build matrix and the
    root causes audited against the CPython source before publishing.
  • A few root causes are explicitly marked partial — the trigger is minimal and verified,
    but the exact offending line wasn't pinned (noted in those reports): OOM-0010, 0027, 0035.
  • OOM-0001 is already filed as
    #151673; the other 34 had no matching
    python/cpython issue when checked and appear novel.

Found with fusil (OOM-injection mode; fusil originally
by Victor Stinner). Drafted with Claude Code; reproducers machine-generated and human-verified.

CPython versions tested on:

CPython main branch

Operating systems tested on:

Linux

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

Python 3.16.0a0 free-threading build (heads/main:15d74068f3a, Jun 18 2026, 16:44:30) [Clang 22.1.2 (1ubuntu1)]

Linked PRs

  • gh-151763: Fix NULL deref in os._path_normpath() #151779
  • gh-151763: Fix OOM-0034 tokenizer offset error handling #151798
  • gh-151763: Fix OOM-0014 crash in _interpchannels._channel_id #151902
  • gh-151763: Fix double decref in _PyPegen_raise_error_known_location() #151931
  • gh-151763: Fix OOM-0013 crash when the parser or compiler fails to allocate #151968
  • gh-151763: Fix _interpreters.capture_exception crash on memory errors #152013
  • gh-151763: Fix possible crash on CodeType deallocation #152034
  • [3.15] gh-151763: Fix possible crash on CodeType deallocation (GH-152034) #152069
  • [3.14] gh-151763: Fix possible crash on CodeType deallocation (GH-152034) #152070
  • [3.13] gh-151763: Fix NULL dereference in os._path_normpath() under OOM (GH-151779) #152094
  • [3.14] gh-151763: Fix NULL dereference in os._path_normpath() under OOM (GH-151779) #152095
  • [3.15] gh-151763: Fix NULL dereference in os._path_normpath() under OOM (GH-151779) #152096
  • gh-151763: Fix crash in _interpqueues.create on MemoryError #152131
  • gh-151763: Fix OOM cleanup of initial thread state #152155
  • [3.15] gh-151763: Fix crash in _interpqueues.create on MemoryError (GH-152131) #152158
  • [3.14] gh-151763: Fix crash in _interpqueues.create on MemoryError (GH-152131) #152159
  • [3.13] gh-151763: Fix crash in _interpqueues.create on MemoryError (GH-152131) #152160
  • gh-151763: Fix debug assertion in unicode subtype cleanup under OOM #152164
  • gh-151763: Fix OOM cleanup of initial thread state #152165
  • [3.15] gh-151763: Fix OOM-0013 crash when the parser or compiler fails to allocate (GH-151968) #152836
  • [3.14] gh-151763: Fix OOM-0013 crash when the parser or compiler fails to allocate (GH-151968) #152837
  • [3.13] gh-151763: Fix OOM-0013 crash when the parser or compiler fails to allocate (GH-151968) #152840
  • gh-151763: Fix dealloc clears the in-flight exception in subtype_dealloc #153548
  • gh-151763: Fix crash in PyList_New() on allocation failure under low memory #154019
  • gh-151763: Restore exception if parser recovery path leaves none set (OOM-0021) #154717
  • [3.15] gh-151763: Fix crash in PyList_New() on allocation failure under low memory (GH-154019) #161785
  • [3.14] gh-151763: Fix crash in PyList_New() on allocation failure under low memory (GH-154019) #161786

Activity

Next
  1. devdanzin
    added
    type-crashA hard crash of the interpreter, possibly with a core dumpA hard crash of the interpreter, possibly with a core dump
    interpreter-core(Objects, Python, Grammar, and Parser dirs)(Objects, Python, Grammar, and Parser dirs)
    extension-modulesC modules in the Modules dirC modules in the Modules dir
    topic-free-threading
    on Jun 20, 2026
  2. devdanzin
    added a sub-issue on Jun 20, 2026
    • Segfault: Py_DECREF(NULL) in setup_context/do_warn (_warnings.c) when emitting a warning under MemoryError #151673
  3. zainnadeem786 commented on Jun 20, 2026

    @zainnadeem786
    zainnadeem786
    on Jun 20, 2026
    Contributor
    More actions

    Hi @devdanzin ,

    I'd like to investigate and work on a couple of the findings from this umbrella issue.

    I spent some time reviewing the relevant code paths and, based on source inspection, I'm currently looking at:

    • OOM-0028 (os__path_normpath_impl in Modules/posixmodule.c)
    • OOM-0034 (_PyPegen_byte_offset_to_character_offset_line in Parser/pegen.c)

    OOM-0028

    My investigation suggests that there is a potential OOM error path in os__path_normpath_impl() where result may become NULL after Unicode object creation fails, but execution can still proceed into the bytes-path encoding logic. This appears to be a localized fix with conventional NULL propagation semantics.

    OOM-0034

    For OOM-0034, I found that PyUnicode_AsUTF8() may return NULL under allocation failure, while the returned pointer is later used without a guard. However, this one appears slightly more involved because the surrounding tokenization path may also need proper error propagation rather than only adding a local NULL check.

    Next Steps

    My plan is:

    1. Prepare a focused PR for OOM-0028 with a minimal fix and regression coverage if feasible.
    2. Investigate and prepare a separate PR for OOM-0034, ensuring that any MemoryError is propagated correctly through the caller chain rather than only preventing the immediate crash.

    I'll continue investigating these code paths and will post updates here as I make progress. If the analysis holds up, I'll prepare focused PRs for the relevant fixes and link them in this issue.

    Thanks for collecting and documenting these findings.

  4. prakashsellathurai commented on Jun 20, 2026

    @prakashsellathurai
    prakashsellathurai
    on Jun 20, 2026
    Contributor
    More actions

    working on OOM-0002 issue: #151773

  5. StanFromIreland
    removed
    topic-free-threading
    on Jun 20, 2026
  6. zainnadeem786
    added 2 commits that reference this issue on Jun 20, 2026

    pythongh-151763: Add NEWS entry for OOM-0028 fix

    ef4a99a

    pythongh-151763: Move NEWS entry to Library

    73a2b50
  7. devdanzin
    added a sub-issue on Jun 20, 2026
    • Segfault: Py_DECREF(NULL) in PyContextVar_Set (context.c:367) #151773
  8. StanFromIreland
    added a sub-issue on Jun 20, 2026
    • Segfault: dealloc of uninitialized iterator in template_iter (Objects/templateobject.c:232) #151815
  9. amruthamodela06 commented on Jun 21, 2026

    @amruthamodela06
    amruthamodela06
    on Jun 21, 2026
    Contributor
    More actions

    Took OOM-0031: issue #151842, PR #151843.

  10. devdanzin
    added a sub-issue on Jun 21, 2026
    • Segfault: NULL info deref in _excinfo_clear_type via _PyXI_FreeExcInfo(NULL) (Python/crossinterp.c:1319) #151842
  11. zainnadeem786 commented on Jun 21, 2026

    @zainnadeem786
    zainnadeem786
    on Jun 21, 2026
    Contributor
    More actions

    I’d like to take OOM-0014 if no one else is already working on it.

    I reproduced it locally on both debug and release builds.

    Summary of my investigation:

    • _interpchannels._channel_id(0) calls channelsmod__channel_id().
    • channelsmod__channel_id() calls get_module_from_owned_type(cls).
    • That path can return NULL under OOM because _get_current_module() allocates the module name with PyUnicode_FromString(MODULE_NAME_STR).
    • channelsmod__channel_id() currently treats that return value as infallible:
    PyObject *mod = get_module_from_owned_type(cls);
    assert(mod == self);
    Py_DECREF(mod);

    When the allocation fails:

    • debug build aborts at assert(mod == self) because mod == NULL
    • release build reaches Py_DECREF(NULL) and crashes with an access violation

    The minimal fix appears to be a local NULL check:

    PyObject *mod = get_module_from_owned_type(cls);
    if (mod == NULL) {
        return NULL;
    }
    assert(mod == self);
    Py_DECREF(mod);

    This preserves the existing invariant check and correctly propagates the pending MemoryError.

    I also verified the focused test suite still passes locally:

    PCbuild/amd64/python_d.exe -m test test__interpchannels
    Result: SUCCESS, 73 tests run, 5 skipped
    

    I’ll prepare a focused PR with the NULL check and a small subprocess regression test unless there are concerns with this approach.

  12. 38 remaining items

  13. Abhi210 commented on Jul 11, 2026

    @Abhi210
    Abhi210
    on Jul 11, 2026
    Contributor
    More actions

    @devdanzin Thank you for the reply! I found a fix for the issue above, which is available in OOM-0018. While investigating further, I found that the new issue is related to OOM-0004. Applying that fix exposed yet another issue that appears to be independent, so I'll open a separate issue to track it.

    Edit: I resolved the last bug. It's an exact match for #151818, which was already open with fixes in flight (#152114, #152117). I pulled #152114's fix and #152117's regression test onto my tree, and with all four fixes stacked (OOM-0023 - OOM-0018- OOM-0004- #151818), the original sweep now runs clean end-to-end with no crash at all. I would open a PR for OOM-0004 if no one else takes it.

  14. devdanzin commented on Jul 11, 2026

    @devdanzin
    devdanzin
    on Jul 11, 2026
    MemberAuthor
    More actions

    @Abhi210 Wow, impressive investigation and fixing, thank you! Please go ahead and open a PR for OOM-0004.

    Reacted by Abhishek Tiwari
  15. flameastro commented on Jul 11, 2026

    @flameastro
    flameastro
    on Jul 11, 2026 · Hidden as spam
    show commentMore actions
  16. devdanzin
    added a sub-issue on Jul 11, 2026
    • Segfault: NULL deref in os__path_normpath_impl (posixmodule.c:6149) #152058
  17. StanFromIreland
    added a sub-issue on Jul 17, 2026
    • stale MemoryError trips _Py_CheckSlotResult in reload_singlephase_extension #153797
  18. Abhi210
    added 2 commits that reference this issue on Jul 18, 2026

    Fixed OOM-0004 from pythongh-151763

    a452992

    Fixed OOM-0004 from pythongh-151763

    6ff08d9
  19. BHUVANSH855 commented on Oct 9, 2026

    @BHUVANSH855
    BHUVANSH855
    on Oct 9, 2026
    Contributor
    More actions

    Another uninit-dealloc on the OOM path, same pattern as OOM-0024:

    odictiter_new, Objects/odictobject.c:1945

    PyObject_GC_New succeeds but di->di_odict, di->di_current, and di->kind are uninitialized. The next allocation (di->di_result) fails → Py_DECREF(di) → odictiter_dealloc reads garbage fields → crash.

    Reproducer (needs freelist drain — tuple freelist masks index 0):

    import _testcapi, collections
    
    live = [tuple([None, None]) for _ in range(4000)]  # drain freelist
    od = collections.OrderedDict(a=1)
    
    for start in range(20):
        _testcapi.set_nomemory(start, start + 1)
        try:
            iter(od.items())
        except MemoryError:
            pass
        finally:
            _testcapi.remove_mem_hooks()

    Confirmed on main 1a852139408, Linux, Python 3.16.0a0.

    Found with cpython-review-toolkit (maintained by @devdanzin and myself).

  20. kevinprimeraquecreo-stack
    added this to Project_VKon Oct 18, 2026
  21. github-project-automation
    moved this to Todo in Project_VKon Oct 18, 2026
  22. kevinprimeraquecreo-stack
    added this to Project_VKon Oct 18, 2026
  23. Eclips4
    added 3 commits that reference this issue on Nov 10, 2026

    gh-151763: Fix crash in PyList_New() on allocation failure under low …

    acab532

    [3.15] gh-151763: Fix crash in PyList_New() on allocation failure und…

    066c80c

    [3.14] gh-151763: Fix crash in PyList_New() on allocation failure und…

    4e242df
  24. orsenthil
    added a commit that references this issue on Nov 12, 2026

    pythongh-151763: Fix crash in PyList_New() on allocation failure unde…

    00cdf1d
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 dirC modules in the Modules dirinterpreter-core(Objects, Python, Grammar, and Parser dirs)(Objects, Python, Grammar, and Parser dirs)type-crashA hard crash of the interpreter, possibly with a core dumpA 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

      You are visitor 01402520 — GeezerVison does not store the pages you look up.
      Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.