[Python-Dev] another dict crasher
Michael Hudson
mwh@python.net
02 Jun 2001 12:40:56 +0100
"Tim Peters" <tim.one@home.com> writes:
> > I can't easily see other examples of the problem; there certainly
> > might be things you could do with comparisons that could trigger
> > crashes, but that code's so hairy that it's almost impossible for me
> > to be sure.
>
> It's easy to be sure: any code that tries to remember anything about a dict
> (ditto any mutable object) across a "dangerous" call, other than the mere
> address of the object, is a place you *can* provoke a core dump. It may not
> be easy to provoke, and a given provoking test case may not fail across all
> platforms, or even every time you run it on a single platform, but it's "an
> obvious" hole all the same.
Ah, like this one:
dict = {}
# let's force dict to malloc its table
for i in range(1,10):
dict[i] = i
class Machiavelli2:
def __eq__(self, other):
dict.clear()
return 1
def __hash__(self):
return 0
dict[Machiavelli2()] = Machiavelli2()
print dict[Machiavelli2()]
I'll attach a patch, but it's another branch inside lookdict (though
not lookdict_string which is I guess the really performance sensitive
one).
Cheers,
M.
Index: dictobject.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Objects/dictobject.c,v
retrieving revision 2.100
diff -c -1 -r2.100 dictobject.c
*** dictobject.c 2001/06/02 08:27:39 2.100
--- dictobject.c 2001/06/02 11:36:47
***************
*** 273,274 ****
--- 273,281 ----
cmp = PyObject_RichCompareBool(ep->me_key, key, Py_EQ);
+ if (ep0 != mp->ma_table) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "dict resized on comparison");
+ ep = mp->ma_table;
+ while (ep->me_value) ep++;
+ return ep;
+ }
if (cmp > 0) {
***************
*** 310,311 ****
--- 317,325 ----
cmp = PyObject_RichCompareBool(ep->me_key, key, Py_EQ);
+ if (ep0 != mp->ma_table) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "dict resized on comparison");
+ ep = mp->ma_table;
+ while (ep->me_value) ep++;
+ return ep;
+ }
if (cmp > 0) {
Here's another test case to work out the second of those new if
statements:
dict = {}
# let's force dict to malloc its table
for i in range(1,10):
dict[i] = i
class Machiavelli3:
def __init__(self, id):
self.id = id
def __eq__(self, other):
if self.id == other.id:
dict.clear()
return 1
else:
return 0
def __repr__(self):
return "%s(%s)"%(self.__class__.__name__, self.id)
def __hash__(self):
return 0
dict[Machiavelli3(1)] = Machiavelli3(0)
dict[Machiavelli3(2)] = Machiavelli3(0)
print dict[Machiavelli3(2)]
--
M-x psych[TAB][RETURN]
-- try it