From 23cf6be23cc4cd1efa8b7100ff1a6a179cb16092 Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Sat, 2 Jun 2001 08:02:56 +0000 Subject: [PATCH] Coredumpers from Michael Hudson, mutating dicts while printing or converting to string. Critical bugfix candidate -- if you take this seriously . --- Lib/test/test_mutants.py | 66 +++++++++++++++++++++++++++++++++++++++- Objects/dictobject.c | 26 +++++++++++----- 2 files changed, 84 insertions(+), 8 deletions(-) diff --git a/Lib/test/test_mutants.py b/Lib/test/test_mutants.py index 3cd35d17a5f..f981fe67b09 100644 --- a/Lib/test/test_mutants.py +++ b/Lib/test/test_mutants.py @@ -1,5 +1,6 @@ -from test_support import verbose +from test_support import verbose, TESTFN import random +import os # From SF bug #422121: Insecurities in dict comparison. @@ -151,3 +152,66 @@ def test(n): # See last comment block for clues about good values for n. test(100) + +########################################################################## +# Another segfault bug, distilled by Micheal Hundson from a c.l.py post. + +class Child: + def __init__(self, parent): + self.__dict__['parent'] = parent + def __getattr__(self, attr): + self.parent.a = 1 + self.parent.b = 1 + self.parent.c = 1 + self.parent.d = 1 + self.parent.e = 1 + self.parent.f = 1 + self.parent.g = 1 + self.parent.h = 1 + self.parent.i = 1 + return getattr(self.parent, attr) + +class Parent: + def __init__(self): + self.a = Child(self) + +# Hard to say what this will print! May vary from time to time. But +# we're specifically trying to test the tp_print slot here, and this is +# the clearest way to do it. We print the result to a temp file so that +# the expected-output file doesn't need to change. + +f = open(TESTFN, "w") +print >> f, Parent().__dict__ +f.close() +os.unlink(TESTFN) + +########################################################################## +# And another core-dumper from Michael Hudson. + +dict = {} + +# Force dict to malloc its table. +for i in range(1, 10): + dict[i] = i + +f = open(TESTFN, "w") + +class Machiavelli: + def __repr__(self): + dict.clear() + + # Michael sez: "doesn't crash without this. don't know why." + # Tim sez: "luck of the draw; crashes with or without for me." + print >> f + + return `"machiavelli"` + + def __hash__(self): + return 0 + +dict[Machiavelli()] = Machiavelli() + +print >> f, str(dict) +f.close() +os.unlink(TESTFN) +del f, dict diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 857b3c6ffd5..8b58166ca43 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -740,7 +740,6 @@ dict_print(register dictobject *mp, register FILE *fp, register int flags) { register int i; register int any; - register dictentry *ep; i = Py_ReprEnter((PyObject*)mp); if (i != 0) { @@ -752,19 +751,27 @@ dict_print(register dictobject *mp, register FILE *fp, register int flags) fprintf(fp, "{"); any = 0; - for (i = 0, ep = mp->ma_table; i < mp->ma_size; i++, ep++) { - if (ep->me_value != NULL) { + for (i = 0; i < mp->ma_size; i++) { + dictentry *ep = mp->ma_table + i; + PyObject *pvalue = ep->me_value; + if (pvalue != NULL) { + /* Prevent PyObject_Repr from deleting value during + key format */ + Py_INCREF(pvalue); if (any++ > 0) fprintf(fp, ", "); if (PyObject_Print((PyObject *)ep->me_key, fp, 0)!=0) { + Py_DECREF(pvalue); Py_ReprLeave((PyObject*)mp); return -1; } fprintf(fp, ": "); if (PyObject_Print(ep->me_value, fp, 0) != 0) { + Py_DECREF(pvalue); Py_ReprLeave((PyObject*)mp); return -1; } + Py_DECREF(pvalue); } } fprintf(fp, "}"); @@ -779,7 +786,6 @@ dict_repr(dictobject *mp) PyObject *sepa, *colon; register int i; register int any; - register dictentry *ep; i = Py_ReprEnter((PyObject*)mp); if (i != 0) { @@ -792,13 +798,19 @@ dict_repr(dictobject *mp) sepa = PyString_FromString(", "); colon = PyString_FromString(": "); any = 0; - for (i = 0, ep = mp->ma_table; i < mp->ma_size && v; i++, ep++) { - if (ep->me_value != NULL) { + for (i = 0; i < mp->ma_size && v; i++) { + dictentry *ep = mp->ma_table + i; + PyObject *pvalue = ep->me_value; + if (pvalue != NULL) { + /* Prevent PyObject_Repr from deleting value during + key format */ + Py_INCREF(pvalue); if (any++) PyString_Concat(&v, sepa); PyString_ConcatAndDel(&v, PyObject_Repr(ep->me_key)); PyString_Concat(&v, colon); - PyString_ConcatAndDel(&v, PyObject_Repr(ep->me_value)); + PyString_ConcatAndDel(&v, PyObject_Repr(pvalue)); + Py_DECREF(pvalue); } } PyString_ConcatAndDel(&v, PyString_FromString("}"));