From: Tim Peters Date: Mon, 9 Oct 2006 20:24:45 +0000 (+0000) Subject: Backport of the pieces of trunk rev 46589 relevant to X-Git-Tag: v2.4.4c1~10 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2b37ea40871ec45cb1fb6d98a2696bcaf4cf6583;p=thirdparty%2FPython%2Fcpython.git Backport of the pieces of trunk rev 46589 relevant to fixing an unlikely crash bug in dict resizing, SF bug 1456209. The rest of rev 46589 changes whether Python suppresses exceptions during some dict-related comparisons. While I think that's a good idea, it does change visible behavior at times, and there was already some complaining about that on the trunk. Not a good idea for backporting. The part of 46589 checked in here can at worst stop segfaults, and I doubt anyone will gripe about that ;-) --- diff --git a/Lib/test/output/test_operations b/Lib/test/output/test_operations index 32eff3f21a8a..1803a4beb0ec 100644 --- a/Lib/test/output/test_operations +++ b/Lib/test/output/test_operations @@ -4,3 +4,4 @@ XXX Mostly not yet implemented 3.1 Dictionary lookups succeed even if __cmp__() raises an exception raising error No exception passed through. +resize bugs not triggered. diff --git a/Lib/test/test_operations.py b/Lib/test/test_operations.py index b599c9da9e3f..fb1ab8eaea83 100644 --- a/Lib/test/test_operations.py +++ b/Lib/test/test_operations.py @@ -7,9 +7,6 @@ print 'XXX Mostly not yet implemented' print '3.1 Dictionary lookups succeed even if __cmp__() raises an exception' -# SourceForge bug #112558: -# http://sourceforge.net/bugs/?func=detailbug&bug_id=112558&group_id=5470 - class BadDictKey: already_printed_raising_error = 0 @@ -50,3 +47,27 @@ for i in range(5): del d[i] for i in range(5, 9): # i==8 was the problem d[i] = i + + +# Another dict resizing bug (SF bug #1456209). +# This caused Segmentation faults or Illegal instructions. + +class X(object): + def __hash__(self): + return 5 + def __eq__(self, other): + if resizing: + d.clear() + return False +d = {} +resizing = False +d[X()] = 1 +d[X()] = 2 +d[X()] = 3 +d[X()] = 4 +d[X()] = 5 +# now trigger a resize +resizing = True +d[9] = 6 + +print 'resize bugs not triggered.' diff --git a/Misc/NEWS b/Misc/NEWS index 220bbb64c5bf..a2e71ad705a4 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -12,6 +12,11 @@ What's New in Python 2.4.4c1? Core and builtins ----------------- +- Bug #1456209: In some obscure cases it was possible for a class with a + custom ``__eq__()`` method to confuse dict internals when class instances + were used as a dict's keys and the ``__eq__()`` method mutated the dict. + No, you don't have any code that did this ;-) + - A number of places, including integer negation and absolute value, were fixed to not rely on undefined behaviour of the C compiler anymore. @@ -73,7 +78,7 @@ Core and builtins Extension Modules ----------------- -- Fix buffer handling in posix.confstr. +- Fix buffer handling in posix.confstr. - Bug #1572832: fix a bug in ISO-2022 codecs which may cause segfault when encoding non-BMP unicode characters. @@ -142,7 +147,7 @@ Extension Modules Library ------- -- Bug #1545341: The 'classifier' keyword argument to the Distutils setup() +- Bug #1545341: The 'classifier' keyword argument to the Distutils setup() function now accepts tuples as well as lists. - Bug #1560617: in pyclbr, return full module name not only for classes, diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 18d8d5c1afa1..b466abdcf08f 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -417,6 +417,35 @@ insertdict(register dictobject *mp, PyObject *key, long hash, PyObject *value) } } +/* +Internal routine used by dictresize() to insert an item which is +known to be absent from the dict. This routine also assumes that +the dict contains no deleted entries. Besides the performance benefit, +using insertdict() in dictresize() is dangerous (SF bug #1456209). +*/ +static void +insertdict_clean(register dictobject *mp, PyObject *key, long hash, + PyObject *value) +{ + register unsigned int i; + register unsigned int perturb; + register unsigned int mask = mp->ma_mask; + dictentry *ep0 = mp->ma_table; + register dictentry *ep; + + i = hash & mask; + ep = &ep0[i]; + for (perturb = hash; ep->me_key != NULL; perturb >>= PERTURB_SHIFT) { + i = (i << 2) + i + perturb + 1; + ep = &ep0[i & mask]; + } + mp->ma_fill++; + ep->me_key = key; + ep->me_hash = hash; + ep->me_value = value; + mp->ma_used++; +} + /* Restructure the table by allocating a new table and reinserting all items again. When entries have been deleted, the new table may @@ -489,7 +518,8 @@ dictresize(dictobject *mp, int minused) for (ep = oldtable; i > 0; ep++) { if (ep->me_value != NULL) { /* active entry */ --i; - insertdict(mp, ep->me_key, ep->me_hash, ep->me_value); + insertdict_clean(mp, ep->me_key, ep->me_hash, + ep->me_value); } else if (ep->me_key != NULL) { /* dummy entry */ --i;