# PyDict_Merge: # # 1 for (i = 0, n = DK_SIZE(other->ma_keys); i < n; i++) { # ... # 3 entry = &other->ma_keys->dk_entries[i]; # ... # 2 if (insertdict(mp, entry->me_key, # entry->me_hash, # value) != 0) # return -1; # ... # } # # 1. n is set once # 2. it's possible to run a custom __eq__ method from inside the insertdict. # __eq__ clears the "other" dict. "n" variables is now out of date # 3. out of bounds read # # CRASH: # ------ # # * thread #1: tid = 27715, 0x080d1c1d python`insertdict(mp=0xb71d66f4, key=0x61682044, hash=543582496, value=0xb71d6664) + 132 at dictobject.c:819, name = 'python', stop reason = invalid address (fault address: 0x61682050) # frame #0: 0x080d1c1d python`insertdict(mp=0xb71d66f4, key=0x61682044, hash=543582496, value=0xb71d6664) + 132 at dictobject.c:819 # 816 if (ep == NULL) { # 817 return -1; # 818 } # -> 819 assert(PyUnicode_CheckExact(key) || mp->ma_keys->dk_lookup == lookdict); # 820 Py_INCREF(value); # 821 MAINTAIN_TRACKING(mp, key, value); # 822 old_value = *value_addr; # class X: def __hash__(self): print("__hash__ ") return 0 def __eq__(self, o): global other print("__eq__") other.clear() return 0 l = [(i,0) for i in range(1, 1337)] other = dict(l) other[X()] = 0 d = {X(): 0, 1: 1} d.update(other)