Repository navigation
Null pointer dereference in element_[ass_]subscr via re-entrant calls #143200
Copy link
Copy link
Closed
Labels
extension-modulesC modules in the Modules dirC modules in the Modules dirtopic-XMLtype-crashA hard crash of the interpreter, possibly with a core dumpA hard crash of the interpreter, possibly with a core dump
Description
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
- gh-143200: fix UAFs in
Element.__{set,get}item__when the element is concurrently mutated #143226 - [3.14] gh-143200: fix UAFs in
Element.__{set,get}item__when the element is concurrently mutated (GH-143226) #143273 - [3.13] gh-143200: fix UAFs in
Element.__{set,get}item__when the element is concurrently mutated (GH-143226) #143274
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
extension-modulesC modules in the Modules dirC modules in the Modules dirtopic-XMLtype-crashA hard crash of the interpreter, possibly with a core dumpA hard crash of the interpreter, possibly with a core dump
Activity
jackfromeast commentedon Dec 26, 2025
Similar to #143199, but with a different reentrant call.
element_ass_subscrvia re-entrant slice assignment #143199 as a duplicate of this issuepicnixz commentedon Dec 26, 2025
I'm going to close #143199 as a duplicate. In the future, if the affected function is the same, just create a single issue instead of many. Also, please use the "crash" template for reporting those issues. TiA!
[-]Null pointer dereference in `element_ass_subscr` via re-entrant `__index__`[/-][+]Null pointer dereference in `element_ass_subscr` via re-entrant calls[/+]jackfromeast commentedon Dec 26, 2025
Thanks for the explanation! I was using the reentrant call location to deduplicate. Next time, I will use their living functions instead.
picnixz commentedon Dec 26, 2025
You can include the different places where there is an issue, but it's easier to have fewer issues for triaging.
superboy-zjc commentedon Dec 27, 2025
@picnixz hi there, this is actually not duplicated of #143199 as the affected function here should be
element_subscr()(slice handling), not the one discussed in this issue.Here is my proposed fix by adding a NULL pointer check after the possible re-entrant operation.
I’m new to contributing to CPython but happy to work on the PR + a regression test if this direction makes sense. Please let me know what you’d like to see.
picnixz commentedon Dec 27, 2025
For XML UAFs I do not think it is an easy issue for beginners because there is the Python implementation but thanks for pinpointing the exact cause. I will take care of this myself as I already fixed UAFs for XML in the past.
EDIT: Even if they are not clear duplicate, I'll just consider them under the same issue (that is,
__setitem__/__getitem__).[-]Null pointer dereference in `element_ass_subscr` via re-entrant calls[/-][+]Null pointer dereference in `element_[ass]_subscr` via re-entrant calls[/+][-]Null pointer dereference in `element_[ass]_subscr` via re-entrant calls[/-][+]Null pointer dereference in `element_[ass_]subscr` via re-entrant calls[/+]jackfromeast commentedon Dec 27, 2025
Thank you, @superboy-zjc, for pointing out the issue! The original attached code snippets and sanitizer report do not belong to this PoC, and I have just updated both of them :>
Also, thank you, @picnixz, for helping update the title and patch the issue.
pythongh-143200: fix UAFs in `Element.__{set,get}__item`
pythongh-143200: fix UAFs in `Element.__{set,get}item__`
pythongh-143200: fix UAFs in `Element.__{set,get}item__`
picnixz commentedon Dec 27, 2025
To summarize:
__getitem__or__setitem__element_ass_subscrvia re-entrant slice assignment #143199). This only affects__setitem__.gh-143200: fix UAFs in `Element.__{set,get}item__` when the element i…
pythongh-143200: fix UAFs in `Element.__{set,get}item__` when the ele…
pythongh-143200: fix UAFs in `Element.__{set,get}item__` when the ele…
[3.14] gh-143200: fix UAFs in `Element.__{set,get}item__` when the el…
picnixz commentedon Dec 29, 2025
Thanks for the report!
[3.13] gh-143200: fix UAFs in `Element.__{set,get}item__` when the el…
pythongh-143200: fix UAFs in `Element.__{set,get}item__` when the ele…