Use-after-free in lsprof `initContext` via re-entrant external timer `__index__`
未关闭
还没有人认领这个 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
贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 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