What happened?
Passing a list that contains an object whose __str__ clears the list lets _tkinter’s AsObj recurse through PyObject_Str, which mutates the list while the outer loop still walks it. The subsequent PySequence_Fast_GET_ITEM hits a NULL slot and crashes the interpreter.
Proof of Concept:
import tkinter interp = tkinter.Tcl() victim = [] class Evil: def __str__(self): victim.clear() return "boom" victim[:] = [Evil(), "pad"] interp.call('set', 'var', victim)
Affected Versions
Details| Python Version | Status | Exit Code |
|---|---|---|
Python 3.9.24+ (heads/3.9:111bbc15b26, Oct 28 2025, 16:51:20) |
ASAN | 1 |
Python 3.10.19+ (heads/3.10:014261980b1, Oct 28 2025, 16:52:08) [Clang 18.1.3 (1ubuntu1)] |
ASAN | 1 |
Python 3.11.14+ (heads/3.11:88f3f5b5f11, Oct 28 2025, 16:53:08) [Clang 18.1.3 (1ubuntu1)] |
ASAN | 1 |
Python 3.12.12+ (heads/3.12:8cb2092bd8c, Oct 28 2025, 16:54:14) [Clang 18.1.3 (1ubuntu1)] |
ASAN | 1 |
Python 3.13.9+ (heads/3.13:9c8eade20c6, Oct 28 2025, 16:55:18) [Clang 18.1.3 (1ubuntu1)] |
ASAN | 1 |
Python 3.14.0+ (heads/3.14:2e216728038, Oct 28 2025, 16:56:16) [Clang 18.1.3 (1ubuntu1)] |
ASAN | 1 |
Python 3.15.0a1+ (heads/main:f5394c257ce, Oct 28 2025, 16:57:16) [Clang 18.1.3 (1ubuntu1)] |
ASAN | 1 |
Vulnerable Code
Details/* Buggy Re-entrant Path */ static PyObject * Tkapp_Call(PyObject *selfptr, PyObject *args) { Tcl_Obj *objStore[ARGSZ]; Tcl_Obj **objv = NULL; Tcl_Size objc; /* ... */ objv = Tkapp_CallArgs(args, objStore, &objc); if (!objv) { return NULL; } /* ... */ } static Tcl_Obj** Tkapp_CallArgs(PyObject *args, Tcl_Obj** objStore, Tcl_Size *pobjc) { Tcl_Obj **objv = objStore; Py_ssize_t objc = 0, i; /* ... */ for (i = 0; i < objc; i++) { PyObject *v = PySequence_Fast_GET_ITEM(args, i); objv[i] = AsObj(v); /* ... */ } /* ... */ } static Tcl_Obj* AsObj(PyObject *value) { Tcl_Obj *result; /* ... */ if (PyTuple_Check(value) || PyList_Check(value)) { Tcl_Obj **argv; Py_ssize_t size, i; size = PySequence_Fast_GET_SIZE(value); /* crashing pointer derived: ((PyListObject *)value)->ob_item */ argv = (Tcl_Obj **) PyMem_Malloc(((size_t)size) * sizeof(Tcl_Obj *)); if (!argv) { PyErr_NoMemory(); return NULL; } for (i = 0; i < size; i++) { argv[i] = AsObj(PySequence_Fast_GET_ITEM(value, i)); /* Crash site */ } result = Tcl_NewListObj((int)size, argv); PyMem_Free(argv); return result; } /* ... */ { PyObject *v = PyObject_Str(value); /* Reentrant call site */ if (!v) return 0; result = AsObj(v); Py_DECREF(v); return result; } } /* Clobbering Path */ static void list_clear_impl(PyListObject *a, bool is_resize) { PyObject **items = a->ob_item; if (items == NULL) { return; } /* ... */ Py_ssize_t i = Py_SIZE(a); Py_SET_SIZE(a, 0); FT_ATOMIC_STORE_PTR_RELEASE(a->ob_item, NULL); /* state mutate site */ a->allocated = 0; while (--i >= 0) { Py_XDECREF(items[i]); } /* ... */ } static PyObject * py_list_clear_impl(PyListObject *self) { list_clear(self); Py_RETURN_NONE; }
Sanitizer Output
Details=================================================================
==1649088==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000008 (pc 0x7bdb62a40dc6 bp 0x7fffc6b37870 sp 0x7fffc6b377a0 T0)
==1649088==The signal is caused by a READ memory access.
==1649088==Hint: address points to the zero page.
#0 0x7bdb62a40dc6 in AsObj /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/./Modules/_tkinter.c:1011:27
#1 0x7bdb62a3f9a8 in Tkapp_CallArgs /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/./Modules/_tkinter.c:1325:23
#2 0x7bdb62a3237d in Tkapp_Call /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/./Modules/_tkinter.c:1509:16
#3 0x5bcab7d8cfdb in cfunction_call /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Objects/methodobject.c:575:18
#4 0x5bcab7bc4b81 in _PyObject_MakeTpCall /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Objects/call.c:242:18
#5 0x5bcab81c4c62 in _PyEval_EvalFrameDefault /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Python/generated_cases.c.h:1620:35
#6 0x5bcab8193bf4 in _PyEval_Vector /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Python/ceval.c:2005:12
#7 0x5bcab8193bf4 in PyEval_EvalCode /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Python/ceval.c:888:21
#8 0x5bcab84584d4 in run_mod /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Python/pythonrun.c:1459:19
#9 0x5bcab845202d in pyrun_file /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Python/pythonrun.c:1293:15
#10 0x5bcab844f2d3 in _PyRun_SimpleFileObject /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Python/pythonrun.c:521:13
#11 0x5bcab844e89e in _PyRun_AnyFileObject /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Python/pythonrun.c:81:15
#12 0x5bcab8513b13 in pymain_run_file_obj /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Modules/main.c:410:15
#13 0x5bcab8513b13 in pymain_run_file /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Modules/main.c:429:15
#14 0x5bcab8510bcb in pymain_run_python /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Modules/main.c:691:21
#15 0x5bcab8510bcb in Py_RunMain /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Modules/main.c:772:5
#16 0x5bcab85127fb in pymain_main /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Modules/main.c:802:12
#17 0x5bcab8512aa2 in Py_BytesMain /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Modules/main.c:826:12
#18 0x7bdb6622a1c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#19 0x7bdb6622a28a in __libc_start_main csu/../csu/libc-start.c:360:3
#20 0x5bcab78cf114 in _start (/home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/python+0x6b0114) (BuildId: 0aee20a59f1c25de22733bd0e5f8259ab04406c4)
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/./Modules/_tkinter.c:1011:27 in AsObj
==1649088==ABORTING