Commits

Anonymous committed d33e401

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 ;-)

  • Participants
  • Parent commits 3835695
  • Branches 2.4

Comments (0)

Files changed (4)

Lib/test/output/test_operations

 3.1 Dictionary lookups succeed even if __cmp__() raises an exception
 raising error
 No exception passed through.
+resize bugs not triggered.

Lib/test/test_operations.py

 
 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
 
     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.'
 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.
 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.
 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,

Objects/dictobject.c

 }
 
 /*
+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
 actually be smaller than the old one.
 	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;