python / python/cpython

`NULL` pointer dereference in `FutureIter_am_send`

未關閉
#146,065 2 則留言 0 個 reaction 已指派 0 人 在 GitHub 檢視

還沒有人認領這個 Issue。

extension-modules topic-asyncio type-crash
主要語言
Python
星號
77.2k
分支
36k
PR 合併指標
PR 指標待擷取

描述

Crash report

What happened?

It's possible to segfault the interpreter by running the MRE below.

Automated diagnosis:

Bug: NULL pointer dereference in FutureIter_am_send (_asynciomodule.c:1877). FutureIter_am_send passes it->future to Py_BEGIN_CRITICAL_SECTION() without checking for NULL. FutureIter_throw (line 1973) and FutureIter_close (line 1997) both call Py_CLEAR(self->future), setting it->future = NULL. After throw() or close(), any subsequent send()/next() dereferences NULL.

Fix: Add NULL check at the top of FutureIter_am_send:

    if (it->future == NULL) {
        PyErr_SetNone(PyExc_StopIteration);
        *result = NULL;
        return PYGEN_RETURN;
    }

MRE:

import asyncio

async def exploit():
    loop = asyncio.get_event_loop()
    fut = loop.create_future()

    it = fut.__await__()
    it.__next__()       # yields the future, sets fut_blocking

    try:
        it.throw(RuntimeError)  # Py_CLEAR(self->future) -> NULL
    except RuntimeError:
        pass

    it.send(None)       # SEGFAULT: Py_BEGIN_CRITICAL_SECTION(NULL)

asyncio.run(exploit())

Backtrace:

Program received signal SIGSEGV, Segmentation fault.
0x00005555560eb282 in FutureIter_am_send_lock_held (it=0x7c5ff71cf700, result=<optimized out>) at ./Modules/_asynciomodule.c:1849
1849        if (fut->fut_state == STATE_PENDING) {
(gdb) bt
#0  0x00005555560eb282 in FutureIter_am_send_lock_held (it=0x7c5ff71cf700, result=<optimized out>) at ./Modules/_asynciomodule.c:1849
#1  FutureIter_am_send (op=0x7c5ff71cf700, _unused_arg=<optimized out>, result=<optimized out>) at ./Modules/_asynciomodule.c:1878
#2  FutureIter_iternext (it=0x7c5ff71cf700) at ./Modules/_asynciomodule.c:1888
#3  0x0000555555ae6c75 in method_vectorcall_O (func=func@entry=0x7c7ff7322040, args=args@entry=0x7bfff5d36b28, nargsf=nargsf@entry=9223372036854775810, kwnames=kwnames@entry=0x0)
    at Objects/descrobject.c:476
#4  0x0000555555ab9e00 in _PyObject_VectorcallTstate (tstate=0x5555568f7b18 <_PyRuntime+360664>, callable=0x7c7ff7322040, args=0x7bfff5d36b28, nargsf=9223372036854775810, kwnames=0x0)
    at ./Include/internal/pycore_call.h:136
#5  0x0000555555e588dd in _Py_VectorCallInstrumentation_StackRefSteal (callable=..., arguments=<optimized out>, total_args=2, kwnames=..., call_instrumentation=<optimized out>,
    frame=<optimized out>, this_instr=<optimized out>, tstate=<optimized out>) at Python/ceval.c:770
#6  0x0000555555e94263 in _PyEval_EvalFrameDefault (tstate=<optimized out>, frame=<optimized out>, throwflag=<optimized out>) at Python/generated_cases.c.h:1838
#7  0x0000555555b1f1bb in _PyEval_EvalFrame (tstate=0x5555568f7b18 <_PyRuntime+360664>, frame=0x7d0ff724c828, throwflag=0) at ./Include/internal/pycore_ceval.h:118
#8  gen_send_ex2 (gen=0x7d0ff724c7e0, arg=0x5555566bcfa0 <_Py_NoneStruct>, presult=0x7bfff5d36380, exc=0) at Objects/genobject.c:280
#9  0x0000555555a6cefc in PyIter_Send (iter=<optimized out>, arg=0x5555566bcfa0 <_Py_NoneStruct>, result=<optimized out>) at Objects/abstract.c:2936
#10 0x00005555560e584a in task_step_impl (state=<optimized out>, task=0x7d4ff704b3b0, exc=0x0) at ./Modules/_asynciomodule.c:3100
#11 0x00005555560e5033 in task_step (state=state@entry=0x7cfff7122d50, task=0x7d4ff704b3b0, exc=0x0) at ./Modules/_asynciomodule.c:3442
#12 0x00005555560e4977 in TaskStepMethWrapper_call (op=0x7c6ff76084d0, args=0x5555568c0b50 <_PyRuntime+135440>, kwds=0x0) at ./Modules/_asynciomodule.c:2112
#13 0x0000555555aba90b in _PyObject_MakeTpCall (tstate=tstate@entry=0x5555568f7b18 <_PyRuntime+360664>, callable=callable@entry=0x7c6ff76084d0, args=0x7c6ff70b2c38, nargs=0,
    keywords=keywords@entry=0x0) at Objects/call.c:242
#14 0x0000555555f165c8 in _PyObject_VectorcallTstate (tstate=0x5555568f7b18 <_PyRuntime+360664>, callable=0x7c6ff76084d0, args=0x7fffffffff01, nargsf=<optimized out>, kwnames=0x0)
    at ./Include/internal/pycore_call.h:134
#15 context_run (self=0x7c7ff737bec0, args=args@entry=0x7c6ff70b2c30, nargs=nargs@entry=1, kwnames=kwnames@entry=0x0) at Python/context.c:727
#16 0x0000555555bde65c in cfunction_vectorcall_FASTCALL_KEYWORDS (func=func@entry=0x7c7ff737c040, args=0x7c6ff70b2c30, nargsf=1, kwnames=kwnames@entry=0x0) at Objects/methodobject.c:465
#17 0x0000555555abc5a0 in _PyVectorcall_Call (tstate=<optimized out>, func=<optimized out>, callable=<optimized out>, tuple=0x7c6ff70b2c10, kwargs=<optimized out>) at Objects/call.c:273
#18 0x0000555555e904e2 in _PyEval_EvalFrameDefault (tstate=<optimized out>, frame=<optimized out>, throwflag=<optimized out>) at Python/generated_cases.c.h:2937
#19 0x0000555555e57778 in _PyEval_EvalFrame (tstate=0x5555568f7b18 <_PyRuntime+360664>, frame=0x7e8ff6fe5220, throwflag=0) at ./Include/internal/pycore_ceval.h:118
#20 _PyEval_Vector (tstate=<optimized out>, func=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=<optimized out>, kwnames=0x0) at Python/ceval.c:2134
#21 0x0000555555e57195 in PyEval_EvalCode (co=<optimized out>, globals=<optimized out>, locals=0x7c7ff70884c0) at Python/ceval.c:681
#22 0x0000555556061fb0 in run_eval_code_obj (tstate=tstate@entry=0x5555568f7b18 <_PyRuntime+360664>, co=co@entry=0x7d1ff701bd50, globals=globals@entry=0x7c7ff70884c0,
    locals=locals@entry=0x7c7ff70884c0) at Python/pythonrun.c:1368
#23 0x000055555606117c in run_mod (mod=<optimized out>, filename=<optimized out>, globals=<optimized out>, locals=<optimized out>, flags=<optimized out>, arena=<optimized out>,
    interactive_src=<optimized out>, generate_new_source=<optimized out>) at Python/pythonrun.c:1471

Found using cpython-review-toolkit with Claude Opus 4.6, using the /cpython-review-toolkit:explore Modules/_asynciomodule.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:99e2c5eccd2, Mar 17 2026, 08:26:50) [Clang 21.1.2 (2ubuntu6)]

Linked PRs
  • gh-146304

貢獻指南

開啟貢獻指南

從這裡開始

  1. 先讀完整個 Issue,再讀專案的貢獻指南。
  2. 在 Issue 下留言說明你要接手 —— 這能避免兩個人做同樣的事。
  3. Fork 儲存庫,在一個分支上完成修改。
  4. 送出 Pull Request,並在描述裡引用這個 Issue 編號。

研究方向

從 Modules/_asynciomodule.c 中的 FutureIter_am_send 開始,檢視報告中描述的現有 throw() 和 close() 路徑。在 CPython main 上重現 MRE,然後為迭代器被清除後的傳送新增覆蓋,並確認它以 StopIteration 結束,而不是發生當機。

由索引模型根據 Issue 內容生成。

評估

技術堆疊
c, python
領域
backend
Issue 類型
缺陷
難度
2/5
預估耗時
1-3 小時
活躍度
停滯
描述清晰度
描述清楚
新手友好度
35/100

把新 issue 寄到你的電子郵件信箱

精選適合新手參與的 GitHub issue 摘要。