CPython crashes on memory-allocation failure (OOM): 35 findings (4 dups → 31 distinct)
Chưa có ai nhận issue này.
- Ngôn ngữ chính
- Python
- Star
- 77.2k
- Fork
- 35.9k
- Chỉ số merge pull request
- Chỉ số pull request đang chờ
Mô tả
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 commit15d7406. 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 runpython 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_nomemoryrequires a test/debug interpreter (--with-pydebugexposes it).- Build matrix (in each report): many of these are debug-only assertion /
Py_FatalError
failures — theassert(...)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, aPy_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
returningNULLis dereferenced (≈ one-line NULL checks). - OOM-0001, OOM-0002, OOM-0014 —
Py_DECREF(NULL)on an unchecked-allocation
error path. - OOM-0031 —
_interpreters.capture_exceptioncalls_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 gh-89373
_Py_Deallocinvariant): OOM-0007
(context_tp_dealloc) and OOM-0023 (the genericsubtype_dealloc, covering a family of
pure-Python types) free an object while aMemoryErroris 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 — sameframe.f_backswallow — and is folded). Several share the theme that the
adaptive specializer / call machinery is entered with aMemoryErroralready pending. Py_DECREF/Py_CLEARof 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_APPENDlist.append()-under-MemoryErrordouble-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-151779
- gh-151798
- gh-151902
- gh-151931
- gh-151968
- gh-152013
- gh-152034
- gh-152069
- gh-152070
- gh-152094
- gh-152095
- gh-152096
- gh-152131
- gh-152155
- gh-152158
- gh-152159
- gh-152160
- gh-152164
- gh-152165
- gh-152836
- gh-152837
- gh-152840
- gh-153548
- gh-154019
- gh-154717
Hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Hướng nghiên cứu
Chọn một phát hiện OOM chưa được xử lý trong bảng và mở gist được liên kết trước. Build một trình thông dịch kiểm thử/gỡ lỗi CPython với --with-pydebug, chạy OOM-ID-repro.py của gist, rồi kiểm tra vị trí mã nguồn và backtrace được nêu. Hoàn tất có nghĩa là tạo một issue hoặc PR tập trung cho phát hiện đó, sau đó thêm liên kết vào issue tổng hợp này.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Đánh giá
- Công nghệ
- python
- Lĩnh vực
- backend
- Loại issue
- Lỗi
- Độ khó
- 5/5
- Thời gian dự kiến
- Hơn một tuần
- Mức độ hoạt động
- Ít trao đổi
- Độ rõ ràng
- Khá rõ ràng
- Mức phù hợp với người mới
- 35/100