python / python/cpython

Use-after-free in lsprof `initContext` via re-entrant external timer `__index__`

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

還沒有人認領這個 Issue。

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

描述

What happened?

prof._pystart_callback installs a new ProfilerContext and immediately calls the external timer. If the timer’s __index__ clears the profiler, it frees currentProfilerContext while initContext still writes to it, leading to a heap use-after-free.

Proof of Concept:

import cProfile

class Timer:
    def __call__(self):
        return self

    def __index__(self):
        prof.clear()
        return 0

prof = cProfile.Profile(timer=Timer())

def victim():
    pass

prof._pystart_callback(victim.__code__, 0)

Vulnerable Code Snippet:

Click to expand
/* Buggy Re-entrant Path */
op(_DO_CALL, (callable, self_or_null, args[oparg] -- res)) {
    /* ... */
    PyObject *res_o = PyObject_Vectorcall(
        callable_o, args_o,
        total_args | PY_VECTORCALL_ARGUMENTS_OFFSET,
        NULL);
    /* ... */
}

static void
initContext(ProfilerObject *pObj, ProfilerContext *self, ProfilerEntry *entry)
{
    self->ctxEntry = entry;
    self->subt = 0;
    self->previous = pObj->currentProfilerContext;
    pObj->currentProfilerContext = self;  /* crashing pointer derived */
    /* ... */
    self->t0 = call_timer(pObj);  /* Reentrant call site */ /* Crash site */
}

static PyTime_t
CallExternalTimer(ProfilerObject *pObj)
{
    PyObject *o;
    /* ... */
    o = _PyObject_CallNoArgs(pObj->externalTimer);
    /* ... */
    err = _PyTime_FromSecondsObject(&result, o, _PyTime_ROUND_FLOOR);
    /* ... */
    return result;
}

static inline PyObject*
vectorcall_unbound(PyThreadState *tstate, int unbound, PyObject *func,
                   PyObject *const *args, Py_ssize_t nargs)
{
    /* ... */
    return _PyObject_VectorcallTstate(tstate, func, args, nargsf, NULL);  /* Reentrant call site */
}

/* Clobbering Path */
static void clearEntries(ProfilerObject *pObj)
{
    /* ... */
    if (pObj->currentProfilerContext) {
        PyMem_Free(pObj->currentProfilerContext);  /* state mutate site */
        pObj->currentProfilerContext = NULL;
    }
    /* ... */
}

Sanitizer Output:

Click to expand
=================================================================
==287983==ERROR: AddressSanitizer: heap-use-after-free on address 0x50300001dc80 at pc 0x78e4bf424a0a bp 0x7ffd850cf040 sp 0x7ffd850cf030
WRITE of size 8 at 0x50300001dc80 thread T0
    #0 0x78e4bf424a09 in initContext Modules/_lsprof.c:324
    #1 0x78e4bf424a09 in ptrace_enter_call Modules/_lsprof.c:393
    #2 0x78e4bf424c51 in _lsprof_Profiler__pystart_callback_impl Modules/_lsprof.c:631
    #3 0x78e4bf424c51 in _lsprof_Profiler__pystart_callback Modules/clinic/_lsprof.c.h:88
    #4 0x5f5f549183e7 in _PyObject_VectorcallTstate Include/internal/pycore_call.h:169
    #5 0x5f5f549183e7 in PyObject_Vectorcall Objects/call.c:327
    #6 0x5f5f547cc5a2 in _PyEval_EvalFrameDefault Python/generated_cases.c.h:1620
    #7 0x5f5f54c96ad6 in _PyEval_EvalFrame Include/internal/pycore_ceval.h:121
    #8 0x5f5f54c96ad6 in _PyEval_Vector Python/ceval.c:2001
    #9 0x5f5f54c96ad6 in PyEval_EvalCode Python/ceval.c:884
    #10 0x5f5f54ddc16e in run_eval_code_obj Python/pythonrun.c:1365
    #11 0x5f5f54ddc16e in run_mod Python/pythonrun.c:1459
    #12 0x5f5f54de0e17 in pyrun_file Python/pythonrun.c:1293
    #13 0x5f5f54de0e17 in _PyRun_SimpleFileObject Python/pythonrun.c:521
    #14 0x5f5f54de193c in _PyRun_AnyFileObject Python/pythonrun.c:81
    #15 0x5f5f54e54e3c in pymain_run_file_obj Modules/main.c:410
    #16 0x5f5f54e54e3c in pymain_run_file Modules/main.c:429
    #17 0x5f5f54e54e3c in pymain_run_python Modules/main.c:691
    #18 0x5f5f54e5671e in Py_RunMain Modules/main.c:772
    #19 0x5f5f54e5671e in pymain_main Modules/main.c:802
    #20 0x5f5f54e5671e in Py_BytesMain Modules/main.c:826
    #21 0x78e4bf22a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #22 0x78e4bf22a28a in __libc_start_main_impl ../csu/libc-start.c:360
    #23 0x5f5f547f0634 in _start (/home/jackfromeast/Desktop/entropy/targets/grammar-afl++-latest/targets/cpython/python+0x206634) (BuildId: 4d105290d0ad566a4d6f4f7b2f05fbc9e317b533)

0x50300001dc80 is located 0 bytes inside of 32-byte region [0x50300001dc80,0x50300001dca0)
freed by thread T0 here:
    #0 0x78e4bf6fc4d8 in free ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:52
    #1 0x78e4bf425706 in clearEntries Modules/_lsprof.c:296
    #2 0x78e4bf425706 in _lsprof_Profiler_clear_impl Modules/_lsprof.c:954
    #3 0x78e4bf425706 in _lsprof_Profiler_clear Modules/clinic/_lsprof.c.h:370
    #4 0x5f5f549183e7 in _PyObject_VectorcallTstate Include/internal/pycore_call.h:169
    #5 0x5f5f549183e7 in PyObject_Vectorcall Objects/call.c:327
    #6 0x5f5f547cc5a2 in _PyEval_EvalFrameDefault Python/generated_cases.c.h:1620
    #7 0x5f5f54c972a5 in _PyEval_EvalFrame Include/internal/pycore_ceval.h:121
    #8 0x5f5f54c972a5 in _PyEval_Vector Python/ceval.c:2001
    #9 0x5f5f54ad3607 in _PyObject_VectorcallTstate Include/internal/pycore_call.h:169
    #10 0x5f5f54ad3607 in vectorcall_unbound Objects/typeobject.c:3033
    #11 0x5f5f54ad3607 in vectorcall_method Objects/typeobject.c:3104
    #12 0x5f5f54ad3607 in slot_nb_index Objects/typeobject.c:10507
    #13 0x5f5f548c413f in _PyNumber_Index Objects/abstract.c:1418
    #14 0x5f5f549cc267 in PyLong_AsLongLong Objects/longobject.c:1592
    #15 0x5f5f54de599a in pytime_from_object Python/pytime.c:593
    #16 0x5f5f54de599a in pytime_from_object Python/pytime.c:589
    #17 0x5f5f54de599a in _PyTime_FromSecondsObject Python/pytime.c:630
    #18 0x78e4bf4232f3 in CallExternalTimer Modules/_lsprof.c:120
    #19 0x78e4bf423f91 in call_timer Modules/_lsprof.c:135
    #20 0x78e4bf423f91 in initContext Modules/_lsprof.c:324
    #21 0x78e4bf423f91 in ptrace_enter_call Modules/_lsprof.c:393
    #22 0x78e4bf424c51 in _lsprof_Profiler__pystart_callback_impl Modules/_lsprof.c:631
    #23 0x78e4bf424c51 in _lsprof_Profiler__pystart_callback Modules/clinic/_lsprof.c.h:88
    #24 0x5f5f549183e7 in _PyObject_VectorcallTstate Include/internal/pycore_call.h:169
    #25 0x5f5f549183e7 in PyObject_Vectorcall Objects/call.c:327
    #26 0x5f5f547cc5a2 in _PyEval_EvalFrameDefault Python/generated_cases.c.h:1620
    #27 0x5f5f54c96ad6 in _PyEval_EvalFrame Include/internal/pycore_ceval.h:121
    #28 0x5f5f54c96ad6 in _PyEval_Vector Python/ceval.c:2001
    #29 0x5f5f54c96ad6 in PyEval_EvalCode Python/ceval.c:884
    #30 0x5f5f54ddc16e in run_eval_code_obj Python/pythonrun.c:1365
    #31 0x5f5f54ddc16e in run_mod Python/pythonrun.c:1459
    #32 0x5f5f54de0e17 in pyrun_file Python/pythonrun.c:1293
    #33 0x5f5f54de0e17 in _PyRun_SimpleFileObject Python/pythonrun.c:521
    #34 0x5f5f54de193c in _PyRun_AnyFileObject Python/pythonrun.c:81
    #35 0x5f5f54e54e3c in pymain_run_file_obj Modules/main.c:410
    #36 0x5f5f54e54e3c in pymain_run_file Modules/main.c:429
    #37 0x5f5f54e54e3c in pymain_run_python Modules/main.c:691
    #38 0x5f5f54e5671e in Py_RunMain Modules/main.c:772
    #39 0x5f5f54e5671e in pymain_main Modules/main.c:802
    #40 0x5f5f54e5671e in Py_BytesMain Modules/main.c:826
    #41 0x78e4bf22a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #42 0x78e4bf22a28a in __libc_start_main_impl ../csu/libc-start.c:360
CPython versions tested on:
Python Version Status Exit Code
Python 3.9.24+ (heads/3.9:111bbc15b26, Oct 28 2025, 16:51:20) Exception 1
Python 3.10.19+ (heads/3.10:014261980b1, Oct 28 2025, 16:52:08) [Clang 18.1.3 (1ubuntu1)] Exception 1
Python 3.11.14+ (heads/3.11:88f3f5b5f11, Oct 28 2025, 16:53:08) [Clang 18.1.3 (1ubuntu1)] Exception 1
Python 3.12.12+ (heads/3.12:8cb2092bd8c, Oct 28 2025, 16:54:14) [Clang 18.1.3 (1ubuntu1)] ASAN 1
Python 3.13.9+ (heads/3.13:9c8eade20c6, Oct 28 2025, 16:55:18) [Clang 18.1.3 (1ubuntu1)] ASAN 1
Python 3.14.0+ (heads/3.14:2e216728038, Oct 28 2025, 16:56:16) [Clang 18.1.3 (1ubuntu1)] ASAN 1
Python 3.15.0a1+ (heads/main:f5394c257ce, Oct 28 2025, 19:29:54) [GCC 13.3.0] ASAN 1
Operating systems tested on:

Linux

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

Python 3.15.0a1+ (heads/main:f5394c257ce, Oct 28 2025, 19:29:54) [GCC 13.3.0]

Linked PRs
  • gh-143550

貢獻指南

開啟貢獻指南

從這裡開始

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

研究方向

從 Modules/_lsprof.c 中的 initContext、CallExternalTimer、clearEntries 以及 __pystart_callback 路徑開始。在 ASAN 建置下重現所提供的 Timer 範例,然後檢查相關的 gh-143550 工作。完成的標準是 re-entrant callback 不再產生 use-after-free,並且有一個回歸測試涵蓋此行為。

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

評估

技術堆疊
c, python
領域
devtools, performance
Issue 類型
缺陷
難度
4/5
預估耗時
3-5 天
活躍度
停滯
描述清晰度
基本清楚
新手友好度
25/100

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

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