Skip to content

Use After Free in deque_index_impl #115243

Closed
@kcatss

Description

@kcatss

Crash report

What happened?

Version

Python 3.13.0a3+ (heads/v3.13.0a2:e2c4038924, Feb 10 2024, 12:05:47) [GCC 11.4.0]
bisect from commit 32ea165

Root cause

the deque_index_impl function retrieves an element from the deque using b→data. However, the reference count of the item may decrease due to PyObject_RichCompareBool, leading to a use-after-free

static PyObject *
deque_index_impl(dequeobject *deque, PyObject *v, Py_ssize_t start,
                 Py_ssize_t stop){
...
	while (n--) {
            CHECK_NOT_END(b);
            item = b->data[index]; // <---  don't raise reference count using Py_NewRef
            cmp = PyObject_RichCompareBool(item, v, Py_EQ); //  <---  arbitrary call to __eq__
            if (cmp > 0)
                return PyLong_FromSsize_t(stop - (n + 1));
            else if (cmp < 0)
                return NULL;
            if (start_state != deque->state) {
                PyErr_SetString(PyExc_RuntimeError,
                                "deque mutated during iteration");
                return NULL;
           }
}

POC

import collections

class evil_pre1(object):
    def __eq__(self,o):
        deq.clear()
        return NotImplemented

deq = collections.deque([evil_pre1()])

deq.index(3)

ASAN

asan
=================================================================
==246599==ERROR: AddressSanitizer: heap-use-after-free on address 0xffffb086b3c8 at pc 0xaaaabb857308 bp 0xffffc5757c40 sp 0xffffc5757c50
READ of size 8 at 0xffffb086b3c8 thread T0
    #0 0xaaaabb857304 in Py_TYPE Include/object.h:333
    #1 0xaaaabb857304 in long_richcompare Objects/longobject.c:3265
    #2 0xaaaabb8c1158 in do_richcompare Objects/object.c:922
    #3 0xaaaabb8c1438 in PyObject_RichCompare Objects/object.c:965
    #4 0xaaaabb8c1578 in PyObject_RichCompareBool Objects/object.c:987
    #5 0xaaaabbcc1a5c in deque_index_impl Modules/_collectionsmodule.c:1222
    #6 0xaaaabbcc1c94 in deque_index Modules/clinic/_collectionsmodule.c.h:248
    #7 0xaaaabb7f4ee4 in method_vectorcall_FASTCALL Objects/descrobject.c:401
    #8 0xaaaabb7ccb84 in _PyObject_VectorcallTstate Include/internal/pycore_call.h:168
    #9 0xaaaabb7cccc0 in PyObject_Vectorcall Objects/call.c:327
    #10 0xaaaabbab73e4 in _PyEval_EvalFrameDefault Python/generated_cases.c.h:815
    #11 0xaaaabbb008f4 in _PyEval_EvalFrame Include/internal/pycore_ceval.h:115
    #12 0xaaaabbb008f4 in _PyEval_Vector Python/ceval.c:1788
    #13 0xaaaabbb00abc in PyEval_EvalCode Python/ceval.c:592
    #14 0xaaaabbc02aec in run_eval_code_obj Python/pythonrun.c:1294
    #15 0xaaaabbc059e0 in run_mod Python/pythonrun.c:1379
    #16 0xaaaabbc0677c in pyrun_file Python/pythonrun.c:1215
    #17 0xaaaabbc08c3c in _PyRun_SimpleFileObject Python/pythonrun.c:464
    #18 0xaaaabbc08ff4 in _PyRun_AnyFileObject Python/pythonrun.c:77
    #19 0xaaaabbc66e68 in pymain_run_file_obj Modules/main.c:357
    #20 0xaaaabbc693d8 in pymain_run_file Modules/main.c:376
    #21 0xaaaabbc69de0 in pymain_run_python Modules/main.c:628
    #22 0xaaaabbc6a084 in Py_RunMain Modules/main.c:707
    #23 0xaaaabbc6a274 in pymain_main Modules/main.c:737
    #24 0xaaaabbc6a5b0 in Py_BytesMain Modules/main.c:761
    #25 0xaaaabb63145c in main Programs/python.c:15
    #26 0xffffb93d73f8 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #27 0xffffb93d74c8 in __libc_start_main_impl ../csu/libc-start.c:392
    #28 0xaaaabb63136c in _start (/home/kk/projects/cpython/python+0x27136c)

0xffffb086b3c8 is located 56 bytes inside of 72-byte region [0xffffb086b390,0xffffb086b3d8)
freed by thread T0 here:
    #0 0xffffb96a9fe8 in __interceptor_free ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:127
    #1 0xaaaabb8c9744 in _PyMem_RawFree Objects/obmalloc.c:84
    #2 0xaaaabb8cc580 in _PyMem_DebugRawFree Objects/obmalloc.c:2398
    #3 0xaaaabb8ccd74 in _PyMem_DebugFree Objects/obmalloc.c:2531
    #4 0xaaaabb8f0f78 in PyObject_Free Objects/obmalloc.c:995
    #5 0xaaaabbb7087c in PyObject_GC_Del Python/gc.c:1903
    #6 0xaaaabb914e1c in object_dealloc Objects/typeobject.c:5569
    #7 0xaaaabb938da8 in subtype_dealloc Objects/typeobject.c:2092
    #8 0xaaaabb8bf0b8 in _Py_Dealloc Objects/object.c:2889
    #9 0xaaaabbb69bc4 in Py_DECREF Include/object.h:922
    #10 0xaaaabbb69bc4 in Py_XDECREF Include/object.h:1030
    #11 0xaaaabbb69bc4 in _PyFrame_ClearExceptCode Python/frame.c:140
    #12 0xaaaabbaa276c in clear_thread_frame Python/ceval.c:1652
    #13 0xaaaabbaab750 in _PyEval_FrameClearAndPop Python/ceval.c:1679
    #14 0xaaaabbadc91c in _PyEval_EvalFrameDefault Python/generated_cases.c.h:4914
    #15 0xaaaabbb008f4 in _PyEval_EvalFrame Include/internal/pycore_ceval.h:115
    #16 0xaaaabbb008f4 in _PyEval_Vector Python/ceval.c:1788
    #17 0xaaaabb7cc2fc in _PyFunction_Vectorcall Objects/call.c:413
    #18 0xaaaabb94bfe0 in _PyObject_VectorcallTstate Include/internal/pycore_call.h:168
    #19 0xaaaabb94bfe0 in vectorcall_unbound Objects/typeobject.c:2271
    #20 0xaaaabb94bfe0 in slot_tp_richcompare Objects/typeobject.c:8983
    #21 0xaaaabb8c1058 in do_richcompare Objects/object.c:916
    #22 0xaaaabb8c1438 in PyObject_RichCompare Objects/object.c:965
    #23 0xaaaabb8c1578 in PyObject_RichCompareBool Objects/object.c:987
    #24 0xaaaabbcc1a5c in deque_index_impl Modules/_collectionsmodule.c:1222
    #25 0xaaaabbcc1c94 in deque_index Modules/clinic/_collectionsmodule.c.h:248
    #26 0xaaaabb7f4ee4 in method_vectorcall_FASTCALL Objects/descrobject.c:401
    #27 0xaaaabb7ccb84 in _PyObject_VectorcallTstate Include/internal/pycore_call.h:168
    #28 0xaaaabb7cccc0 in PyObject_Vectorcall Objects/call.c:327
    #29 0xaaaabbab73e4 in _PyEval_EvalFrameDefault Python/generated_cases.c.h:815
    #30 0xaaaabbb008f4 in _PyEval_EvalFrame Include/internal/pycore_ceval.h:115
    #31 0xaaaabbb008f4 in _PyEval_Vector Python/ceval.c:1788
    #32 0xaaaabbb00abc in PyEval_EvalCode Python/ceval.c:592
    #33 0xaaaabbc02aec in run_eval_code_obj Python/pythonrun.c:1294
    #34 0xaaaabbc059e0 in run_mod Python/pythonrun.c:1379
    #35 0xaaaabbc0677c in pyrun_file Python/pythonrun.c:1215

previously allocated by thread T0 here:
    #0 0xffffb96aa2f4 in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:145
    #1 0xaaaabb8cb160 in _PyMem_RawMalloc Objects/obmalloc.c:56
    #2 0xaaaabb8c9028 in _PyMem_DebugRawAlloc Objects/obmalloc.c:2331
    #3 0xaaaabb8c9084 in _PyMem_DebugRawMalloc Objects/obmalloc.c:2364
    #4 0xaaaabb8ccdc0 in _PyMem_DebugMalloc Objects/obmalloc.c:2516
    #5 0xaaaabb8f0df0 in PyObject_Malloc Objects/obmalloc.c:966
    #6 0xaaaabb92e22c in _PyObject_MallocWithType Include/internal/pycore_object_alloc.h:46
    #7 0xaaaabb92e22c in _PyType_AllocNoTrack Objects/typeobject.c:1739
    #8 0xaaaabb92e538 in PyType_GenericAlloc Objects/typeobject.c:1763
    #9 0xaaaabb92830c in object_new Objects/typeobject.c:5555
    #10 0xaaaabb92ec48 in type_call Objects/typeobject.c:1682
    #11 0xaaaabb7cc64c in _PyObject_MakeTpCall Objects/call.c:242
    #12 0xaaaabb7ccc90 in _PyObject_VectorcallTstate Include/internal/pycore_call.h:166
    #13 0xaaaabb7cccc0 in PyObject_Vectorcall Objects/call.c:327
    #14 0xaaaabbab73e4 in _PyEval_EvalFrameDefault Python/generated_cases.c.h:815
    #15 0xaaaabbb008f4 in _PyEval_EvalFrame Include/internal/pycore_ceval.h:115
    #16 0xaaaabbb008f4 in _PyEval_Vector Python/ceval.c:1788
    #17 0xaaaabbb00abc in PyEval_EvalCode Python/ceval.c:592
    #18 0xaaaabbc02aec in run_eval_code_obj Python/pythonrun.c:1294
    #19 0xaaaabbc059e0 in run_mod Python/pythonrun.c:1379
    #20 0xaaaabbc0677c in pyrun_file Python/pythonrun.c:1215
    #21 0xaaaabbc08c3c in _PyRun_SimpleFileObject Python/pythonrun.c:464
    #22 0xaaaabbc08ff4 in _PyRun_AnyFileObject Python/pythonrun.c:77
    #23 0xaaaabbc66e68 in pymain_run_file_obj Modules/main.c:357
    #24 0xaaaabbc693d8 in pymain_run_file Modules/main.c:376
    #25 0xaaaabbc69de0 in pymain_run_python Modules/main.c:628
    #26 0xaaaabbc6a084 in Py_RunMain Modules/main.c:707
    #27 0xaaaabbc6a274 in pymain_main Modules/main.c:737
    #28 0xaaaabbc6a5b0 in Py_BytesMain Modules/main.c:761
    #29 0xaaaabb63145c in main Programs/python.c:15
    #30 0xffffb93d73f8 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #31 0xffffb93d74c8 in __libc_start_main_impl ../csu/libc-start.c:392

SUMMARY: AddressSanitizer: heap-use-after-free Include/object.h:333 in Py_TYPE
Shadow bytes around the buggy address:
  0x200ff610d620: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x200ff610d630: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x200ff610d640: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x200ff610d650: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x200ff610d660: fa fa fa fa fd fd fd fd fd fd fd fd fd fa fa fa
=>0x200ff610d670: fa fa fd fd fd fd fd fd fd[fd]fd fa fa fa fa fa
  0x200ff610d680: fd fd fd fd fd fd fd fd fd fd fa fa fa fa fd fd
  0x200ff610d690: fd fd fd fd fd fd fd fd fa fa fa fa fd fd fd fd
  0x200ff610d6a0: fd fd fd fd fd fd fa fa fa fa 00 00 00 00 00 00
  0x200ff610d6b0: 00 00 00 00 fa fa fa fa 00 00 00 00 00 00 00 00
  0x200ff610d6c0: 00 00 fa fa fa fa 00 00 00 00 00 00 00 00 00 00
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
==246599==ABORTING

CPython versions tested on:

CPython main branch

Operating systems tested on:

Linux

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

Python 3.13.0a3+ (heads/v3.13.0a2:e2c4038924, Feb 10 2024, 12:05:47) [GCC 11.4.0]

Linked PRs

Metadata

Metadata

Assignees

No one assigned

    Labels

    3.11only security fixes3.12only security fixes3.13bugs and security fixesextension-modulesC modules in the Modules dirtype-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