# _siftdown(PyListObject *heap, Py_ssize_t startpos, Py_ssize_t pos) # ... # newitem = PyList_GET_ITEM(heap, pos); # Py_INCREF(newitem); # /* Follow the path to the root, moving parents down until finding # a place newitem fits. */ # while (pos > startpos){ # parentpos = (pos - 1) >> 1; # 1 parent = PyList_GET_ITEM(heap, parentpos); # 2 cmp = PyObject_RichCompareBool(newitem, parent, Py_LT); # if (cmp == -1) { # Py_DECREF(newitem); # return -1; # } # 3 if (size != PyList_GET_SIZE(heap)) { # Py_DECREF(newitem); # PyErr_SetString(PyExc_RuntimeError, # "list changed size during iteration"); # return -1; # } # if (cmp == 0) # break; # 4 Py_INCREF(parent); # ... # # 1. parent isn't protected (refcnt==1) # 2. custom compare function deletes all objects in "heap" and repopulates it with # fresh instances. "parent" is freed # 3. check is ineffective. Heap was mutated while preserving its size # 4. use after free. Crash will manifest itself later. import heapq as h class X(): def __lt__(self, o): global L n = len(L) print("len(L):", n) for i in range(n): del L[0] L.append(X()) return 1 L = [] for i in range(2): h.heappush(L, X()) print(L)