Skip to content

Null pointer dereference in element_[ass_]subscr via re-entrant calls #143200

@jackfromeast

Description

@jackfromeast

What happened?

A crafted __index__ invoked by root[bad_index:] clears the element so element_ass_subscr frees self->extra, but the slice handling continues and calls PySlice_AdjustIndices on the dangling child store, dereferencing NULL and crashing the interpreter.

Proof of Concept:

import xml.etree.ElementTree as ET

root = ET.Element("root")
root.extend([ET.Element("c0"), ET.Element("c1")])

class Evil:
    def __index__(self):
        # Re-entrant mutation: clear() frees the internal child storage
        global root
        root.clear()
        return 0

bad_index = Evil()
root[bad_index:]
Affected Versions
Python Version Status Exit Code
Python 3.9.24+ (heads/3.9:111bbc15b26, Oct 27 2025, 21:34:13) OK 0
Python 3.10.19+ (heads/3.10:014261980b1, Oct 27 2025, 21:19:00) [Clang 18.1.3 (1ubuntu1)] ASAN 1
Python 3.11.14+ (heads/3.11:88f3f5b5f11, Oct 27 2025, 21:20:35) [Clang 18.1.3 (1ubuntu1)] ASAN 1
Python 3.12.12+ (heads/3.12:8cb2092bd8c, Oct 27 2025, 21:27:07) [Clang 18.1.3 (1ubuntu1)] ASAN 1
Python 3.13.9+ (heads/3.13:9c8eade20c6, Oct 27 2025, 21:28:49) [Clang 18.1.3 (1ubuntu1)] ASAN 1
Python 3.14.0+ (heads/3.14:2e216728038, Oct 27 2025, 21:30:55) [Clang 18.1.3 (1ubuntu1)] ASAN 1
Python 3.15.0a1+ (heads/main:f5394c257ce, Oct 27 2025, 21:32:37) [Clang 18.1.3 (1ubuntu1)] ASAN 1
Vulnerable Code
static PyObject *
element_subscr(PyObject *op, PyObject *item)
{
    ElementObject *self = _Element_CAST(op);

    if (PyIndex_Check(item)) {
        Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);

        if (i == -1 && PyErr_Occurred()) {
            return NULL;
        }
        if (i < 0 && self->extra)
            i += self->extra->length;
        return element_getitem(op, i);
    }
    else if (PySlice_Check(item)) {
        Py_ssize_t start, stop, step, slicelen, i;
        size_t cur;
        PyObject* list;

        if (!self->extra)
            return PyList_New(0);

        /* Call _PyEval_SliceIndex -> _PyNumber_Index and reentrant to the interpreter again. */
        if (PySlice_Unpack(item, &start, &stop, &step) < 0) {
            return NULL;
        }
        /* ... */

        // Crash: self->extra is a null pointer now
        slicelen = PySlice_AdjustIndices(self->extra->length, &start, &stop,
                                         step);
}
Sanitizer Output
AddressSanitizer:DEADLYSIGNAL
=================================================================
==11783==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000008 (pc 0x7638bd19b2a1 bp 0x7ffd815de790 sp 0x7ffd815de690 T0)
==11783==The signal is caused by a READ memory access.
==11783==Hint: address points to the zero page.
    #0 0x7638bd19b2a1 in element_subscr Modules/_elementtree.c:1819
    #1 0x7638bd19b2a1 in element_subscr Modules/_elementtree.c:1794
    #2 0x5f64666f687c in PyObject_GetItem Objects/abstract.c:163
    #3 0x5f64665ec668 in _PyEval_EvalFrameDefault Python/generated_cases.c.h:1143
    #4 0x5f6466ac4ad6 in _PyEval_EvalFrame Include/internal/pycore_ceval.h:121
    #5 0x5f6466ac4ad6 in _PyEval_Vector Python/ceval.c:2001
    #6 0x5f6466ac4ad6 in PyEval_EvalCode Python/ceval.c:884
    #7 0x5f6466c0a16e in run_eval_code_obj Python/pythonrun.c:1365
    #8 0x5f6466c0a16e in run_mod Python/pythonrun.c:1459
    #9 0x5f6466c0ee17 in pyrun_file Python/pythonrun.c:1293
    #10 0x5f6466c0ee17 in _PyRun_SimpleFileObject Python/pythonrun.c:521
    #11 0x5f6466c0f93c in _PyRun_AnyFileObject Python/pythonrun.c:81
    #12 0x5f6466c82e3c in pymain_run_file_obj Modules/main.c:410
    #13 0x5f6466c82e3c in pymain_run_file Modules/main.c:429
    #14 0x5f6466c82e3c in pymain_run_python Modules/main.c:691
    #15 0x5f6466c8471e in Py_RunMain Modules/main.c:772
    #16 0x5f6466c8471e in pymain_main Modules/main.c:802
    #17 0x5f6466c8471e in Py_BytesMain Modules/main.c:826
    #18 0x7638bde2a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #19 0x7638bde2a28a in __libc_start_main_impl ../csu/libc-start.c:360
    #20 0x5f646661e634 in _start (/home/jackfromeast/Desktop/entropy/targets/grammar-afl++-latest/targets/cpython/python+0x206634) (BuildId: 4d105290d0ad566a4d6f4f7b2f05fbc9e317b533)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV Modules/_elementtree.c:1819 in element_subscr
==11783==ABORTING
[Inferior 1 (process 11783) exited with code 01]

Linked PRs

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