-
-
Notifications
You must be signed in to change notification settings - Fork 33.9k
Open
Labels
extension-modulesC modules in the Modules dirC modules in the Modules dirtopic-profilingtopic-replRelated to the interactive shellRelated to the interactive shell
Description
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:
Details
| 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
Metadata
Metadata
Assignees
Labels
extension-modulesC modules in the Modules dirC modules in the Modules dirtopic-profilingtopic-replRelated to the interactive shellRelated to the interactive shell