CPython crashes on memory-allocation failure (OOM): 35 findings (4 dups → 31 distinct)
还没有人认领这个 Issue。
- 主要语言
- Python
- 星标
- 77.2k
- 派生
- 35.9k
- PR 合并指标
- PR 指标待抓取
描述
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
贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
调研方向
从表格中选择一个尚未归档的 OOM 发现,先打开其链接的 gist。使用 --with-pydebug 构建 CPython 测试/调试解释器,运行 gist 中的 OOM-ID-repro.py,并检查所指明的源代码位置和回溯。完成的标准是针对该发现提交一个专门的 issue 或 PR,然后在此总 issue 中添加链接。
由索引模型根据 Issue 内容生成。
评估
- 技术栈
- python
- 领域
- backend
- Issue 类型
- 缺陷
- 难度
- 5/5
- 预计耗时
- 一周以上
- 活跃度
- 冷清
- 描述清晰度
- 基本清楚
- 新手友好度
- 35/100