(thanks go to my partner in crime, jorendorff, for helping flesh this out.) lookdict calls PyObject_RichCompareBool without using INCREF/DECREF on the key passed. It's possible for the comparison to delete the key from the dict, causing its own argument to be deallocated. This can lead to bogus results or a crash. A custom type with its own __eq__ method will implicitly INCREF the key when passing it as an argument, which prevents incorrect behaviour from manifesting. There are a couple ways around this though, as shown in my attachment.
Two questions: * Which versions of Python are vulnerable to the problem? You forgot to fill in the version list. * How do we fix the problem? Is it enough to incref the key or must startkey be protected, too?
I can reproduce the segfault in 2.2 through 2.4; in 2.5 and 2.6 the output is this instead: Test 1, using __eq__(a, b).__nonzero__() this is never the right answer ***** Test 2, using tuple's tp_richcompare New Watch 0xf7f8cbac New Watch 0xf7f8cc0c Deleting Watch 0xf7f8cbac Deleting Watch 0xf7f8cbac Deleting Watch 0xf7f8cc0c Traceback (most recent call last): File "/tmp/db3.py", line 72, in print(d[(Bar(), Watch())]) TypeError: __eq__() takes exactly 1 argument (2 given) which suggests it's still there ("this is never the right answer"). In 3.0 the output from the 1st test is "this is an acceptable answer" suggesting it's no longer there; but I suspect it's there in 3.0 as well but due to the unicode transition the dict code there is different enough that the example doesn't trigger it. The key that needs to be INCREF'ed is actually startkey. Patch attached.
Guido van Rossum wrote: > The key that needs to be INCREF'ed is actually startkey. Patch attached. What about the second PyObject_RichCompareBool(startkey, key, Py_EQ) a few lines below inside the for loop? Christian