Skip to content

Crash: use-after-free in elementiter_next #148625

@kdsjZh

Description

@kdsjZh

Crash report

What happened?

Summary

While Cpython processing user-provided py file (in C),
res = (*f)(w, v, _Py_SwappedOp[op]); may allow the cpython execute
user provided python script again (C->Py), this script can invoke into the
C implementation again (C->py->C), and invalidate some objects that the first
C call stack still being used. Thus, when returning from C to py, and returning from py
to C, the subsequent use of that object may be deleted, yield UAF.

Following the developer's assessment, this bug is not inside the threat model of the
cpython, thus report it as crash.

Details

OS: ubuntu 24.04
download python v3.15.0a8 from github, also reproduced on the v3.14.4

autoconf -ivf
./configure
--prefix="${BUILD_DIR}"
--without-pymalloc
--with-ensurepip=no
--with-address-sanitizer
--with-pydebug
make -j
./python3.15 ./poc.py
// Modules/_elementtree.c

rc = PyObject_RichCompareBool(elem->tag, it->sought_tag, Py_EQ);

// Objects/object.c
// in do_richcompare
if (!Py_IS_TYPE(v, Py_TYPE(w)) &&
    PyType_IsSubtype(Py_TYPE(w), Py_TYPE(v)) &&   
    (f = Py_TYPE(w)->tp_richcompare) != NULL) {
    checked_reverse_op = 1;
    res = (*f)(w, v, _Py_SwappedOp[op]);           // [1] execute user python script again
    if (res != Py_NotImplemented)
        return res;
    Py_DECREF(res);
}
if ((f = Py_TYPE(v)->tp_richcompare) != NULL) {    // [2] reads v->ob_type → UAF!
    res = (*f)(v, w, op);

In [1], the user supplied python child.tag = "x" is executed,
indirectly free the object

When user code execute finish, the [2] read Py_TYPE(v) which acessed a freed object.

Asan stack trace

=================================================================
==2253046==ERROR: AddressSanitizer: heap-use-after-free on address 0x513000059d38 at pc 0x603ae2495f6a bp 0x7ffdbb06e960 sp 0x7ffdbb06e950
READ of size 8 at 0x513000059d38 thread T0
    #0 0x603ae2495f69 in _Py_TYPE Include/object.h:270
    #1 0x603ae2495f69 in do_richcompare Objects/object.c:1059
    #2 0x603ae24961f1 in PyObject_RichCompare Objects/object.c:1109
    #3 0x603ae2496261 in PyObject_RichCompareBool Objects/object.c:1131
    #4 0x7132b2e62a10 in elementiter_next Modules/_elementtree.c:2292
    #5 0x603ae26647d4 in _PyEval_EvalFrameDefault Python/generated_cases.c.h:5747
    #6 0x603ae26918fa in _PyEval_EvalFrame Include/internal/pycore_ceval.h:120
    #7 0x603ae2691bee in _PyEval_Vector Python/ceval.c:2110
    #8 0x603ae2691e9e in PyEval_EvalCode Python/ceval.c:982
    #9 0x603ae2786ea8 in run_eval_code_obj Python/pythonrun.c:1366
    #10 0x603ae27870c4 in run_mod Python/pythonrun.c:1460
    #11 0x603ae2787f1b in pyrun_file Python/pythonrun.c:1294
    #12 0x603ae278abe2 in _PyRun_SimpleFileObject Python/pythonrun.c:521
    #13 0x603ae278aeb8 in _PyRun_AnyFileObject Python/pythonrun.c:81
    #14 0x603ae27e4631 in pymain_run_file_obj Modules/main.c:410
    #15 0x603ae27e4898 in pymain_run_file Modules/main.c:429
    #16 0x603ae27e5fa6 in pymain_run_python Modules/main.c:695
    #17 0x603ae27e6636 in Py_RunMain Modules/main.c:776
    #18 0x603ae27e6822 in pymain_main Modules/main.c:806
    #19 0x603ae27e6ba7 in Py_BytesMain Modules/main.c:830
    #20 0x603ae2263645 in main Programs/python.c:15
    #21 0x7132b522a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #22 0x7132b522a28a in __libc_start_main_impl ../csu/libc-start.c:360
    #23 0x603ae2263574 in _start (/home/kdsj/workspace/project-crosslang/benchmark/cpython/build/v3.14.4-asan/bin/python3.14+0x2c4574) (BuildId: e918d56abcb4f55a10536eb34bee4266005eb07f)

0x513000059d38 is located 56 bytes inside of 352-byte region [0x513000059d00,0x513000059e60)
freed by thread T0 here:
    #0 0x7132b56fc4d8 in free ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:52
    #1 0x603ae249d399 in _PyMem_RawFree Objects/obmalloc.c:91
    #2 0x603ae249f705 in _PyMem_DebugRawFree Objects/obmalloc.c:2959
    #3 0x603ae249f746 in _PyMem_DebugFree Objects/obmalloc.c:3104
    #4 0x603ae24c7aa3 in PyObject_Free Objects/obmalloc.c:1526
    #5 0x603ae26ff8bb in PyObject_GC_Del Python/gc.c:2415
    #6 0x603ae24e2290 in object_dealloc Objects/typeobject.c:6907
    #7 0x603ae2500b48 in subtype_dealloc Objects/typeobject.c:2776
    #8 0x603ae2493bcc in _Py_Dealloc Objects/object.c:3206
    #9 0x603ae26f5490 in Py_DECREF_MORTAL Include/internal/pycore_object.h:450
    #10 0x603ae26f5546 in PyStackRef_XCLOSE Include/internal/pycore_stackref.h:682
    #11 0x603ae26f670b in _PyFrame_ClearLocals Python/frame.c:101
    #12 0x603ae26f6903 in _PyFrame_ClearExceptCode Python/frame.c:126
    #13 0x603ae2637238 in clear_thread_frame Python/ceval.c:1935
    #14 0x603ae263b605 in _PyEval_FrameClearAndPop Python/ceval.c:1959
    #15 0x603ae26847fd in _PyEval_EvalFrameDefault Python/generated_cases.c.h:10620
    #16 0x603ae26918fa in _PyEval_EvalFrame Include/internal/pycore_ceval.h:120
    #17 0x603ae2691bee in _PyEval_Vector Python/ceval.c:2110
    #18 0x603ae23d599e in _PyFunction_Vectorcall Objects/call.c:413
    #19 0x603ae24e9ad5 in _PyObject_VectorcallTstate Include/internal/pycore_call.h:177
    #20 0x603ae25056c5 in vectorcall_unbound Objects/typeobject.c:2957
    #21 0x603ae25056c5 in maybe_call_special_one_arg Objects/typeobject.c:3099
    #22 0x603ae25057f2 in _PyObject_MaybeCallSpecialOneArg Objects/typeobject.c:3114
    #23 0x603ae2505838 in slot_tp_richcompare Objects/typeobject.c:10456
    #24 0x603ae2495ea4 in do_richcompare Objects/object.c:1054
    #25 0x603ae24961f1 in PyObject_RichCompare Objects/object.c:1109
    #26 0x603ae2496261 in PyObject_RichCompareBool Objects/object.c:1131
    #27 0x7132b2e62a10 in elementiter_next Modules/_elementtree.c:2292
    #28 0x603ae26647d4 in _PyEval_EvalFrameDefault Python/generated_cases.c.h:5747
    #29 0x603ae26918fa in _PyEval_EvalFrame Include/internal/pycore_ceval.h:120
    #30 0x603ae2691bee in _PyEval_Vector Python/ceval.c:2110

previously allocated by thread T0 here:
    #0 0x7132b56fd9c7 in malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69
    #1 0x603ae249dcb0 in _PyMem_RawMalloc Objects/obmalloc.c:63
    #2 0x603ae249d081 in _PyMem_DebugRawAlloc Objects/obmalloc.c:2891
    #3 0x603ae249d0e9 in _PyMem_DebugRawMalloc Objects/obmalloc.c:2924
    #4 0x603ae249e967 in _PyMem_DebugMalloc Objects/obmalloc.c:3089
    #5 0x603ae24c795f in PyObject_Malloc Objects/obmalloc.c:1497
    #6 0x603ae24fa4cd in _PyObject_MallocWithType Include/internal/pycore_object_alloc.h:46
    #7 0x603ae24fa4cd in _PyType_AllocNoTrack Objects/typeobject.c:2428
    #8 0x603ae24fa659 in PyType_GenericAlloc Objects/typeobject.c:2459
    #9 0x603ae24f24e7 in object_new Objects/typeobject.c:6897
    #10 0x603ae24fd775 in type_call Objects/typeobject.c:2372
    #11 0x603ae23d5c57 in _PyObject_MakeTpCall Objects/call.c:242
    #12 0x603ae23d5eff in _PyObject_VectorcallTstate Include/internal/pycore_call.h:175
    #13 0x603ae23d5f58 in PyObject_Vectorcall Objects/call.c:327
    #14 0x603ae264ab15 in _PyEval_EvalFrameDefault Python/generated_cases.c.h:1621
    #15 0x603ae26918fa in _PyEval_EvalFrame Include/internal/pycore_ceval.h:120
    #16 0x603ae2691bee in _PyEval_Vector Python/ceval.c:2110
    #17 0x603ae2691e9e in PyEval_EvalCode Python/ceval.c:982
    #18 0x603ae2786ea8 in run_eval_code_obj Python/pythonrun.c:1366
    #19 0x603ae27870c4 in run_mod Python/pythonrun.c:1460
    #20 0x603ae2787f1b in pyrun_file Python/pythonrun.c:1294
    #21 0x603ae278abe2 in _PyRun_SimpleFileObject Python/pythonrun.c:521
    #22 0x603ae278aeb8 in _PyRun_AnyFileObject Python/pythonrun.c:81
    #23 0x603ae27e4631 in pymain_run_file_obj Modules/main.c:410
    #24 0x603ae27e4898 in pymain_run_file Modules/main.c:429
    #25 0x603ae27e5fa6 in pymain_run_python Modules/main.c:695
    #26 0x603ae27e6636 in Py_RunMain Modules/main.c:776
    #27 0x603ae27e6822 in pymain_main Modules/main.c:806
    #28 0x603ae27e6ba7 in Py_BytesMain Modules/main.c:830
    #29 0x603ae2263645 in main Programs/python.c:15
    #30 0x7132b522a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58

SUMMARY: AddressSanitizer: heap-use-after-free Include/object.h:270 in _Py_TYPE
Shadow bytes around the buggy address:
  0x513000059a80: 00 00 00 00 00 00 00 00 00 00 00 00 00 fa fa fa
  0x513000059b00: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
  0x513000059b80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x513000059c00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x513000059c80: 00 00 00 fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x513000059d00: fd fd fd fd fd fd fd[fd]fd fd fd fd fd fd fd fd
  0x513000059d80: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x513000059e00: fd fd fd fd fd fd fd fd fd fd fd fd fa fa fa fa
  0x513000059e80: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
  0x513000059f00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x513000059f80: 00 00 00 00 00 00 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
==2253046==ABORTING
import xml.etree.ElementTree as ET

class TagBase:
    pass

root = ET.Element("root")
child = ET.SubElement(root, "x")
tag = TagBase()
tag_id = id(tag)
child.tag = tag
del tag

class ClassA(TagBase):
    def __eq__(self, other):
        if id(other) == tag_id:
            child.tag = "x"
            return NotImplemented
        return NotImplemented

for _ in root.iter(ClassA()):
    pass

CPython versions tested on:

3.14

Operating systems tested on:

Linux

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

Python 3.15.0a8 (tags/v3.15.0a8:55ea59e7dc3, Apr 15 2026, 14:16:24) [GCC 13.3.0]

Metadata

Metadata

Assignees

Labels

extension-modulesC modules in the Modules dirtopic-XMLtype-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