]> granicus.if.org Git - python/commitdiff
Checkin Tim's fix to an error discussed on python-dev.
authorRaymond Hettinger <python@rcn.com>
Sun, 26 Sep 2004 19:24:20 +0000 (19:24 +0000)
committerRaymond Hettinger <python@rcn.com>
Sun, 26 Sep 2004 19:24:20 +0000 (19:24 +0000)
Also, add a testcase.

Formerly, the list_extend() code used several local variables to remember
its state across iterations.  Since an iteration could call arbitrary
Python code, it was possible for the list state to be changed.  The new
code uses dynamic structure references instead of C locals.  So, they
are always up-to-date.

After list_resize() is called, its size has been updated but the new
cells are filled with NULLs.  These needed to be filled before arbitrary
iteration code was called; otherwise, that code could attempt to modify
a list that was in a semi-invalid state.  The solution was to change
the ob->size field back to a value reflecting the actual number of valid
cells.

Lib/test/test_builtin.py
Objects/listobject.c

index 8e3a925032d4420c8665d289f6ba24204b68ac65..9f909250e861b6a5f3f1577a1a058c7fe0332273 100644 (file)
@@ -712,6 +712,11 @@ class BuiltinTest(unittest.TestCase):
             #     http://sources.redhat.com/ml/newlib/2002/msg00369.html
             self.assertRaises(MemoryError, list, xrange(sys.maxint // 2))
 
+        # This code used to segfault in Py2.4a3
+        x = []
+        x.extend(-y for y in x)
+        self.assertEqual(x, [])
+
     def test_long(self):
         self.assertEqual(long(314), 314L)
         self.assertEqual(long(3.14), 3L)
index 1fb77b9760c3762ec23f075d61950a48cf3f76f5..44616e56a031be77b884cc214d6f2d6153bba3fd 100644 (file)
@@ -769,12 +769,20 @@ listextend(PyListObject *self, PyObject *b)
        }
        m = self->ob_size;
        mn = m + n;
-       if (list_resize(self, mn) == -1)
-               goto error;
-       memset(&(self->ob_item[m]), 0, sizeof(*self->ob_item) * n);
+       if (mn >= m) {
+               /* Make room. */
+               if (list_resize(self, mn) == -1)
+                       goto error;
+               /* Make the list sane again. */
+               self->ob_size = m;
+       }
+       /* Else m + n overflowed; on the chance that n lied, and there really
+        * is enough room, ignore it.  If n was telling the truth, we'll
+        * eventually run out of memory during the loop.
+        */
 
        /* Run iterator to exhaustion. */
-       for (i = m; ; i++) {
+       for (;;) {
                PyObject *item = iternext(it);
                if (item == NULL) {
                        if (PyErr_Occurred()) {
@@ -785,8 +793,11 @@ listextend(PyListObject *self, PyObject *b)
                        }
                        break;
                }
-               if (i < mn)
-                       PyList_SET_ITEM(self, i, item); /* steals ref */
+               if (self->ob_size < self->allocated) {
+                       /* steals ref */
+                       PyList_SET_ITEM(self, self->ob_size, item);
+                       ++self->ob_size;
+               }
                else {
                        int status = app1(self, item);
                        Py_DECREF(item);  /* append creates a new ref */
@@ -796,10 +807,9 @@ listextend(PyListObject *self, PyObject *b)
        }
 
        /* Cut back result list if initial guess was too large. */
-       if (i < mn && self != NULL) {
-               if (list_ass_slice(self, i, mn, (PyObject *)NULL) != 0)
-                       goto error;
-       }
+       if (self->ob_size < self->allocated)
+               list_resize(self, self->ob_size);  /* shrinking can't fail */
+
        Py_DECREF(it);
        Py_RETURN_NONE;