Skip to content

Use After Free in initContext(_lsprof.c) #120289

Closed
@kcatss

Description

@kcatss

Crash report

What happened?

Version
Python 3.14.0a0 (heads/main:34f5ae69fe, Jun 9 2024, 21:27:54) [GCC 11.4.0]
bisect from commit 2158977

Root Cause

The call_timer function can execute arbitrary code from pObj, which is initialized by the user. If the code calls _lsprof_type_Profiler_post1.disable() in Python, it will hit profiler_disable in C. Then, the self (ProfileContext) will be freed. Consequently, after call_timer returns, self->t0 will cause a use-after-free error.

static void
initContext(ProfilerObject *pObj, ProfilerContext *self, ProfilerEntry *entry)
{
    self->ctxEntry = entry;
    self->subt = 0;
    self->previous = pObj->currentProfilerContext;
    pObj->currentProfilerContext = self;
    ++entry->recursionLevel;
    if ((pObj->flags & POF_SUBCALLS) && self->previous) {
        /* find or create an entry for me in my caller's entry */
        ProfilerEntry *caller = self->previous->ctxEntry;
        ProfilerSubEntry *subentry = getSubEntry(pObj, caller, entry);
        if (subentry == NULL)
            subentry = newSubEntry(pObj, caller, entry);
        if (subentry)
            ++subentry->recursionLevel;
    }
    self->t0 = call_timer(pObj); // <-- execute arbitrary code
}

POC

import _lsprof
class evil():
    def __call__(self):
        _lsprof_type_Profiler_post1.disable()
        return True
_lsprof_type_Profiler_post1 = _lsprof.Profiler(evil())
_lsprof_type_Profiler_post1.enable()

print ("dummy")

ASAN

asan
=================================================================
==21434==ERROR: AddressSanitizer: heap-use-after-free on address 0x6060000626d0 at pc 0x7f2a7eaa4f22 bp 0x7ffe7a876d30 sp 0x7ffe7a876d20
WRITE of size 8 at 0x6060000626d0 thread T0
    #0 0x7f2a7eaa4f21 in initContext Modules/_lsprof.c:310
    #1 0x7f2a7eaa684f in ptrace_enter_call Modules/_lsprof.c:379
    #2 0x7f2a7eaa8098 in ccall_callback Modules/_lsprof.c:653
    #3 0x563c6a7ce4da in cfunction_vectorcall_FASTCALL Objects/methodobject.c:425
    #4 0x563c6ab10cb2 in _PyObject_VectorcallTstate Include/internal/pycore_call.h:168
    #5 0x563c6ab10cb2 in call_one_instrument Python/instrumentation.c:907
    #6 0x563c6ab1258b in call_instrumentation_vector Python/instrumentation.c:1095
    #7 0x563c6ab1663d in _Py_call_instrumentation_2args Python/instrumentation.c:1150
    #8 0x563c6aa2b5c5 in _PyEval_EvalFrameDefault Python/generated_cases.c.h:3229
    #9 0x563c6aa4ca7b in _PyEval_EvalFrame Include/internal/pycore_ceval.h:119
    #10 0x563c6aa4ca7b in _PyEval_Vector Python/ceval.c:1819
    #11 0x563c6aa4cc9c in PyEval_EvalCode Python/ceval.c:599
    #12 0x563c6ab64c51 in run_eval_code_obj Python/pythonrun.c:1292
    #13 0x563c6ab67b96 in run_mod Python/pythonrun.c:1377
    #14 0x563c6ab68976 in pyrun_file Python/pythonrun.c:1210
    #15 0x563c6ab6ae55 in _PyRun_SimpleFileObject Python/pythonrun.c:459
    #16 0x563c6ab6b349 in _PyRun_AnyFileObject Python/pythonrun.c:77
    #17 0x563c6abcc718 in pymain_run_file_obj Modules/main.c:357
    #18 0x563c6abcefea in pymain_run_file Modules/main.c:376
    #19 0x563c6abcfbfb in pymain_run_python Modules/main.c:639
    #20 0x563c6abcfd8b in Py_RunMain Modules/main.c:718
    #21 0x563c6abcff72 in pymain_main Modules/main.c:748
    #22 0x563c6abd02ea in Py_BytesMain Modules/main.c:772
    #23 0x563c6a539b15 in main Programs/python.c:15
    #24 0x7f2a81d33d8f  (/lib/x86_64-linux-gnu/libc.so.6+0x29d8f)
    #25 0x7f2a81d33e3f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x29e3f)
    #26 0x563c6a539a44 in _start (/home/kcats/cpython/python+0x282a44)

0x6060000626d0 is located 16 bytes inside of 56-byte region [0x6060000626c0,0x6060000626f8)
freed by thread T0 here:
    #0 0x7f2a820ce537 in __interceptor_free ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:127
    #1 0x563c6a7e49c5 in _PyMem_RawFree Objects/obmalloc.c:90
    #2 0x563c6a7e6c2f in _PyMem_DebugRawFree Objects/obmalloc.c:2754
    #3 0x563c6a7e755d in _PyMem_DebugFree Objects/obmalloc.c:2891
    #4 0x563c6a81abad in PyMem_Free Objects/obmalloc.c:1010
    #5 0x7f2a7eaa4c21 in flush_unmatched Modules/_lsprof.c:766
    #6 0x7f2a7eaa6d4f in profiler_disable Modules/_lsprof.c:815
    #7 0x563c6a70183f in method_vectorcall_NOARGS Objects/descrobject.c:447
    #8 0x563c6a6d7bb9 in _PyObject_VectorcallTstate Include/internal/pycore_call.h:168
    #9 0x563c6a6d7d14 in PyObject_Vectorcall Objects/call.c:327
    #10 0x563c6aa148c4 in _PyEval_EvalFrameDefault Python/generated_cases.c.h:813
    #11 0x563c6aa4ca7b in _PyEval_EvalFrame Include/internal/pycore_ceval.h:119
    #12 0x563c6aa4ca7b in _PyEval_Vector Python/ceval.c:1819
    #13 0x563c6a6d720d in _PyFunction_Vectorcall Objects/call.c:413
    #14 0x563c6a6dbe79 in _PyObject_VectorcallDictTstate Objects/call.c:135
    #15 0x563c6a6dc351 in _PyObject_Call_Prepend Objects/call.c:504
    #16 0x563c6a876cc8 in slot_tp_call Objects/typeobject.c:9668
    #17 0x563c6a6d75e7 in _PyObject_MakeTpCall Objects/call.c:242
    #18 0x7f2a7eaa452d in _PyObject_VectorcallTstate Include/internal/pycore_call.h:166
    #19 0x7f2a7eaa452d in _PyObject_CallNoArgs Include/internal/pycore_call.h:184
    #20 0x7f2a7eaa452d in CallExternalTimer Modules/_lsprof.c:90
    #21 0x7f2a7eaa4e1c in call_timer Modules/_lsprof.c:121
    #22 0x7f2a7eaa4e1c in initContext Modules/_lsprof.c:310
    #23 0x7f2a7eaa684f in ptrace_enter_call Modules/_lsprof.c:379
    #24 0x7f2a7eaa8098 in ccall_callback Modules/_lsprof.c:653
    #25 0x563c6a7ce4da in cfunction_vectorcall_FASTCALL Objects/methodobject.c:425
    #26 0x563c6ab10cb2 in _PyObject_VectorcallTstate Include/internal/pycore_call.h:168
    #27 0x563c6ab10cb2 in call_one_instrument Python/instrumentation.c:907
    #28 0x563c6ab1258b in call_instrumentation_vector Python/instrumentation.c:1095
    #29 0x563c6ab1663d in _Py_call_instrumentation_2args Python/instrumentation.c:1150
    #30 0x563c6aa2b5c5 in _PyEval_EvalFrameDefault Python/generated_cases.c.h:3229
    #31 0x563c6aa4ca7b in _PyEval_EvalFrame Include/internal/pycore_ceval.h:119
    #32 0x563c6aa4ca7b in _PyEval_Vector Python/ceval.c:1819
    #33 0x563c6aa4cc9c in PyEval_EvalCode Python/ceval.c:599
    #34 0x563c6ab64c51 in run_eval_code_obj Python/pythonrun.c:1292
    #35 0x563c6ab67b96 in run_mod Python/pythonrun.c:1377

previously allocated by thread T0 here:
    #0 0x7f2a820ce887 in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:145
    #1 0x563c6a7e556c in _PyMem_RawMalloc Objects/obmalloc.c:62
    #2 0x563c6a7e489f in _PyMem_DebugRawAlloc Objects/obmalloc.c:2686
    #3 0x563c6a7e4907 in _PyMem_DebugRawMalloc Objects/obmalloc.c:2719
    #4 0x563c6a7e759f in _PyMem_DebugMalloc Objects/obmalloc.c:2876
    #5 0x563c6a81aa69 in PyMem_Malloc Objects/obmalloc.c:981
    #6 0x7f2a7eaa6892 in ptrace_enter_call Modules/_lsprof.c:373
    #7 0x7f2a7eaa8098 in ccall_callback Modules/_lsprof.c:653
    #8 0x563c6a7ce4da in cfunction_vectorcall_FASTCALL Objects/methodobject.c:425
    #9 0x563c6ab10cb2 in _PyObject_VectorcallTstate Include/internal/pycore_call.h:168
    #10 0x563c6ab10cb2 in call_one_instrument Python/instrumentation.c:907
    #11 0x563c6ab1258b in call_instrumentation_vector Python/instrumentation.c:1095
    #12 0x563c6ab1663d in _Py_call_instrumentation_2args Python/instrumentation.c:1150
    #13 0x563c6aa2b5c5 in _PyEval_EvalFrameDefault Python/generated_cases.c.h:3229
    #14 0x563c6aa4ca7b in _PyEval_EvalFrame Include/internal/pycore_ceval.h:119
    #15 0x563c6aa4ca7b in _PyEval_Vector Python/ceval.c:1819
    #16 0x563c6aa4cc9c in PyEval_EvalCode Python/ceval.c:599
    #17 0x563c6ab64c51 in run_eval_code_obj Python/pythonrun.c:1292
    #18 0x563c6ab67b96 in run_mod Python/pythonrun.c:1377
    #19 0x563c6ab68976 in pyrun_file Python/pythonrun.c:1210
    #20 0x563c6ab6ae55 in _PyRun_SimpleFileObject Python/pythonrun.c:459
    #21 0x563c6ab6b349 in _PyRun_AnyFileObject Python/pythonrun.c:77
    #22 0x563c6abcc718 in pymain_run_file_obj Modules/main.c:357
    #23 0x563c6abcefea in pymain_run_file Modules/main.c:376
    #24 0x563c6abcfbfb in pymain_run_python Modules/main.c:639
    #25 0x563c6abcfd8b in Py_RunMain Modules/main.c:718
    #26 0x563c6abcff72 in pymain_main Modules/main.c:748
    #27 0x563c6abd02ea in Py_BytesMain Modules/main.c:772
    #28 0x563c6a539b15 in main Programs/python.c:15
    #29 0x7f2a81d33d8f  (/lib/x86_64-linux-gnu/libc.so.6+0x29d8f)

SUMMARY: AddressSanitizer: heap-use-after-free Modules/_lsprof.c:310 in initContext
Shadow bytes around the buggy address:
  0x0c0c80004480: fa fa fa fa fd fd fd fd fd fd fd fa fa fa fa fa
  0x0c0c80004490: 00 00 00 00 00 00 00 fa fa fa fa fa fd fd fd fd
  0x0c0c800044a0: fd fd fd fa fa fa fa fa fd fd fd fd fd fd fd fa
  0x0c0c800044b0: fa fa fa fa fd fd fd fd fd fd fd fa fa fa fa fa
  0x0c0c800044c0: fd fd fd fd fd fd fd fa fa fa fa fa fd fd fd fd
=>0x0c0c800044d0: fd fd fd fa fa fa fa fa fd fd[fd]fd fd fd fd fa
  0x0c0c800044e0: fa fa fa fa fd fd fd fd fd fd fd fa fa fa fa fa
  0x0c0c800044f0: fd fd fd fd fd fd fd fa fa fa fa fa fd fd fd fd
  0x0c0c80004500: fd fd fd fa fa fa fa fa fd fd fd fd fd fd fd fa
  0x0c0c80004510: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c0c80004520: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==21434==ABORTING

CPython versions tested on:

CPython main branch

Operating systems tested on:

Linux

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

Python 3.14.0a0 (heads/main:34f5ae69fe, Jun 9 2024, 21:27:54) [GCC 11.4.0]

Linked PRs

Metadata

Metadata

Assignees

No one assigned

    Labels

    3.14bugs and security fixestype-crashA hard crash of the interpreter, possibly with a core dump

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions