Commit 5ccd8648 authored by Tim Peters's avatar Tim Peters

BTreeItems_seek(): Even if i == pseudoindex on entry, bucket mutation

since the last call may have left us with an invalid index.  Raise
RuntimeError instead of segfaulting (or picking up random trash) if so.
A new (and previously segfaulting) test case is courtesy of Steve
Alexander.
parent 401feaeb
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
****************************************************************************/ ****************************************************************************/
#define BTREEITEMSTEMPLATE_C "$Id: BTreeItemsTemplate.c,v 1.17 2002/06/22 17:22:54 tim_one Exp $\n" #define BTREEITEMSTEMPLATE_C "$Id: BTreeItemsTemplate.c,v 1.18 2003/01/31 20:13:02 tim_one Exp $\n"
/* A BTreeItems struct is returned from calling .items(), .keys() or /* A BTreeItems struct is returned from calling .items(), .keys() or
* .values() on a BTree-based data structure, and is also the result of * .values() on a BTree-based data structure, and is also the result of
...@@ -137,6 +137,7 @@ BTreeItems_seek(BTreeItems *self, int i) ...@@ -137,6 +137,7 @@ BTreeItems_seek(BTreeItems *self, int i)
{ {
int delta, pseudoindex, currentoffset; int delta, pseudoindex, currentoffset;
Bucket *b, *currentbucket; Bucket *b, *currentbucket;
int error;
pseudoindex = self->pseudoindex; pseudoindex = self->pseudoindex;
currentoffset = self->currentoffset; currentoffset = self->currentoffset;
...@@ -210,6 +211,21 @@ BTreeItems_seek(BTreeItems *self, int i) ...@@ -210,6 +211,21 @@ BTreeItems_seek(BTreeItems *self, int i)
} }
assert(pseudoindex == i); assert(pseudoindex == i);
/* Alas, the user may have mutated the bucket since the last time we
* were called, and if they deleted stuff, we may be pointing into
* trash memory now.
*/
PER_USE_OR_RETURN(currentbucket, -1);
error = currentoffset < 0 || currentoffset >= currentbucket->len;
PER_ALLOW_DEACTIVATION(currentbucket);
PER_ACCESSED(currentbucket);
if (error) {
PyErr_SetString(PyExc_RuntimeError,
"the bucket being iterated changed size");
return -1;
}
Py_INCREF(currentbucket); Py_INCREF(currentbucket);
Py_DECREF(self->currentbucket); Py_DECREF(self->currentbucket);
self->currentbucket = currentbucket; self->currentbucket = currentbucket;
......
...@@ -770,6 +770,30 @@ class BTreeTests(MappingBase): ...@@ -770,6 +770,30 @@ class BTreeTests(MappingBase):
self.assertEqual(t.insert(1, 1) , 1) self.assertEqual(t.insert(1, 1) , 1)
self.assertEqual(lsubtract(list(t.keys()), [0,1]) , []) self.assertEqual(lsubtract(list(t.keys()), [0,1]) , [])
def testDamagedIterator(self):
# A cute one from Steve Alexander. This caused the BTreeItems
# object to go insane, accessing memory beyond the allocated part
# of the bucket. If it fails, the symptom is either a C-level
# assertion error (if the BTree code was compiled without NDEBUG),
# or most likely a segfault (if the BTree code was compiled with
# NDEBUG).
t = self.t.__class__()
self._populate(t, 10)
# In order for this to fail, it's important that k be a "lazy"
# iterator, referring to the BTree by indirect position (index)
# instead of a fully materialized list. Then the position can
# end up pointing into trash memory, if the bucket pointed to
# shrinks.
k = t.keys()
for dummy in range(20):
try:
del t[k[0]]
except RuntimeError, detail:
self.assertEqual(str(detail), "the bucket being iterated "
"changed size")
break
## BTree tests ## BTree tests
class TestIOBTrees(BTreeTests, TestCase): class TestIOBTrees(BTreeTests, TestCase):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment