]> granicus.if.org Git - python/commitdiff
Issue #4583: crash after resizing an array.array which has buffer exports.
authorAntoine Pitrou <solipsis@pitrou.net>
Thu, 18 Dec 2008 17:08:32 +0000 (17:08 +0000)
committerAntoine Pitrou <solipsis@pitrou.net>
Thu, 18 Dec 2008 17:08:32 +0000 (17:08 +0000)
Lib/test/test_array.py
Misc/NEWS
Modules/arraymodule.c

index ff9026a0c03afd94012a02c89b6ce81ad874f9e3..5a505de7ec29f5c1008f940b535e41f1ac495f7b 100755 (executable)
@@ -8,6 +8,7 @@ from test import support
 from weakref import proxy
 import array, io, math
 from pickle import loads, dumps
+import operator
 
 class ArraySubclass(array.array):
     pass
@@ -709,8 +710,23 @@ class BaseTest(unittest.TestCase):
 
     def test_buffer(self):
         a = array.array(self.typecode, self.example)
-        b = bytes(memoryview(a))
+        m = memoryview(a)
+        b = bytes(m)
+        self.assertEqual(b, a.tostring())
         self.assertEqual(b[0], a.tostring()[0])
+        # Resizing is forbidden when there are buffer exports
+        self.assertRaises(BufferError, a.append, a[0])
+        self.assertRaises(BufferError, a.extend, a[0:1])
+        self.assertRaises(BufferError, a.remove, a[0])
+        self.assertRaises(BufferError, a.fromlist, a.tolist())
+        self.assertRaises(BufferError, a.fromstring, a.tostring())
+        if self.typecode == 'u':
+            self.assertRaises(BufferError, a.fromunicode, a.tounicode())
+        self.assertRaises(BufferError, operator.setitem, a, slice(0, 0), a)
+        self.assertRaises(BufferError, operator.delitem, a, 0)
+        self.assertRaises(BufferError, operator.delitem, a, slice(0, 1))
+        self.assertRaises(BufferError, operator.irepeat, a, 2)
+        self.assertRaises(BufferError, operator.irepeat, a, 0)
 
     def test_weakref(self):
         s = array.array(self.typecode, self.example)
index eb9a2bfffbc6e4906232105191e91d41f1053105..9274a834a9da84ee795d65d62f876984afe18f17 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -7,7 +7,7 @@ Python News
 What's New in Python 3.1 alpha 0
 ================================
 
-*Release date: XX-XXX-2008*
+*Release date: XX-XXX-200X*
 
 Core and Builtins
 -----------------
@@ -132,6 +132,10 @@ Extension Modules
 - Issues #3167, #3682: Fix test_math failures for log, log10 on Solaris,
   OpenBSD.
 
+- Issue #4583: array.array would not always prohibit resizing when a buffer
+  has been exported, resulting in an interpreter crash when accessing the
+  buffer.
+
 Build
 -----
 
index 16a7a7e198387ef248ebbd0ebea842dccd0cc994..c8b40bd00a35b2e6117abb6d5bbd30dc205f87cc 100644 (file)
@@ -49,9 +49,15 @@ array_resize(arrayobject *self, Py_ssize_t newsize)
        char *items;
        size_t _new_size;
 
+       if (self->ob_exports > 0 && newsize != Py_SIZE(self)) {
+               PyErr_SetString(PyExc_BufferError, 
+                       "cannot resize an array that is exporting buffers");
+               return -1;
+       }
+
        /* Bypass realloc() when a previous overallocation is large enough
           to accommodate the newsize.  If the newsize is 16 smaller than the
-          current size, then proceed with the realloc() to shrink the list.
+          current size, then proceed with the realloc() to shrink the array.
        */
 
        if (self->allocated >= newsize &&
@@ -61,11 +67,13 @@ array_resize(arrayobject *self, Py_ssize_t newsize)
                return 0;
        }
 
-        if (self->ob_exports > 0) {
-                PyErr_SetString(PyExc_BufferError, 
-                                "cannot resize an array that is exporting data");
-                return -1;
-        }
+       if (newsize == 0) {
+               PyMem_FREE(self->ob_item);
+               self->ob_item = NULL;
+               Py_SIZE(self) = 0;
+               self->allocated = 0;
+               return 0;
+       }
 
        /* This over-allocates proportional to the array size, making room
         * for additional growth.  The over-allocation is mild, but is
@@ -731,25 +739,15 @@ array_ass_slice(arrayobject *a, Py_ssize_t ilow, Py_ssize_t ihigh, PyObject *v)
                memmove(item + (ihigh+d)*a->ob_descr->itemsize,
                        item + ihigh*a->ob_descr->itemsize,
                        (Py_SIZE(a)-ihigh)*a->ob_descr->itemsize);
-               Py_SIZE(a) += d;
-               PyMem_RESIZE(item, char, Py_SIZE(a)*a->ob_descr->itemsize);
-                                               /* Can't fail */
-               a->ob_item = item;
-               a->allocated = Py_SIZE(a);
+               if (array_resize(a, Py_SIZE(a) + d) == -1)
+                   return -1;
        }
        else if (d > 0) { /* Insert d items */
-               PyMem_RESIZE(item, char,
-                            (Py_SIZE(a) + d)*a->ob_descr->itemsize);
-               if (item == NULL) {
-                       PyErr_NoMemory();
+               if (array_resize(a, Py_SIZE(a) + d))
                        return -1;
-               }
                memmove(item + (ihigh+d)*a->ob_descr->itemsize,
                        item + ihigh*a->ob_descr->itemsize,
                        (Py_SIZE(a)-ihigh)*a->ob_descr->itemsize);
-               a->ob_item = item;
-               Py_SIZE(a) += d;
-               a->allocated = Py_SIZE(a);
        }
        if (n > 0)
                memcpy(item + ilow*a->ob_descr->itemsize, b->ob_item,
@@ -804,8 +802,7 @@ array_iter_extend(arrayobject *self, PyObject *bb)
 static int
 array_do_extend(arrayobject *self, PyObject *bb)
 {
-       Py_ssize_t size;
-       char *old_item;
+       Py_ssize_t size, oldsize;
 
        if (!array_Check(bb))
                return array_iter_extend(self, bb);
@@ -820,18 +817,12 @@ array_do_extend(arrayobject *self, PyObject *bb)
                PyErr_NoMemory();
                return -1;
        }
-       size = Py_SIZE(self) + Py_SIZE(b);
-       old_item = self->ob_item;
-        PyMem_RESIZE(self->ob_item, char, size*self->ob_descr->itemsize);
-        if (self->ob_item == NULL) {
-               self->ob_item = old_item;
-               PyErr_NoMemory();
+       oldsize = Py_SIZE(self);
+       size = oldsize + Py_SIZE(b);
+       if (array_resize(self, size) == -1)
                return -1;
-        }
-       memcpy(self->ob_item + Py_SIZE(self)*self->ob_descr->itemsize,
-               b->ob_item, Py_SIZE(b)*b->ob_descr->itemsize);
-       Py_SIZE(self) = size;
-       self->allocated = size;
+       memcpy(self->ob_item + oldsize * self->ob_descr->itemsize,
+               b->ob_item, Py_SIZE(b) * b->ob_descr->itemsize);
 
        return 0;
 #undef b
@@ -867,27 +858,15 @@ array_inplace_repeat(arrayobject *self, Py_ssize_t n)
                        return PyErr_NoMemory();
                }
                size = Py_SIZE(self) * self->ob_descr->itemsize;
-               if (n == 0) {
-                       PyMem_FREE(items);
-                       self->ob_item = NULL;
-                       Py_SIZE(self) = 0;
-                       self->allocated = 0;
+               if (n > 0 && size > PY_SSIZE_T_MAX / n) {
+                       return PyErr_NoMemory();
                }
-               else {
-                       if (size > PY_SSIZE_T_MAX / n) {
-                               return PyErr_NoMemory();
-                       }
-                       PyMem_RESIZE(items, char, n * size);
-                       if (items == NULL)
-                               return PyErr_NoMemory();
-                       p = items;
-                       for (i = 1; i < n; i++) {
-                               p += size;
-                               memcpy(p, items, size);
-                       }
-                       self->ob_item = items;
-                       Py_SIZE(self) *= n;
-                       self->allocated = Py_SIZE(self);
+               if (array_resize(self, n * Py_SIZE(self)) == -1)
+                       return NULL;
+               items = p = self->ob_item;
+               for (i = 1; i < n; i++) {
+                       p += size;
+                       memcpy(p, items, size);
                }
        }
        Py_INCREF(self);
@@ -1312,7 +1291,6 @@ static PyObject *
 array_fromlist(arrayobject *self, PyObject *list)
 {
        Py_ssize_t n;
-       Py_ssize_t itemsize = self->ob_descr->itemsize;
 
        if (!PyList_Check(list)) {
                PyErr_SetString(PyExc_TypeError, "arg must be list");
@@ -1320,28 +1298,15 @@ array_fromlist(arrayobject *self, PyObject *list)
        }
        n = PyList_Size(list);
        if (n > 0) {
-               char *item = self->ob_item;
-               Py_ssize_t i;
-               PyMem_RESIZE(item, char, (Py_SIZE(self) + n) * itemsize);
-               if (item == NULL) {
-                       PyErr_NoMemory();
+               Py_ssize_t i, old_size;
+               old_size = Py_SIZE(self);
+               if (array_resize(self, old_size + n) == -1)
                        return NULL;
-               }
-               self->ob_item = item;
-               Py_SIZE(self) += n;
-               self->allocated = Py_SIZE(self);
                for (i = 0; i < n; i++) {
                        PyObject *v = PyList_GetItem(list, i);
                        if ((*self->ob_descr->setitem)(self,
                                        Py_SIZE(self) - n + i, v) != 0) {
-                               Py_SIZE(self) -= n;
-                               if (itemsize && (Py_SIZE(self) > PY_SSIZE_T_MAX / itemsize)) {
-                                       return PyErr_NoMemory();
-                               }
-                               PyMem_RESIZE(item, char,
-                                                 Py_SIZE(self) * itemsize);
-                               self->ob_item = item;
-                               self->allocated = Py_SIZE(self);
+                               array_resize(self, old_size);
                                return NULL;
                        }
                }
@@ -1395,21 +1360,15 @@ array_fromstring(arrayobject *self, PyObject *args)
        }
        n = n / itemsize;
        if (n > 0) {
-               char *item = self->ob_item;
-               if ((n > PY_SSIZE_T_MAX - Py_SIZE(self)) ||
-                       ((Py_SIZE(self) + n) > PY_SSIZE_T_MAX / itemsize)) {
+        Py_ssize_t old_size = Py_SIZE(self);
+               if ((n > PY_SSIZE_T_MAX - old_size) ||
+                       ((old_size + n) > PY_SSIZE_T_MAX / itemsize)) {
                                return PyErr_NoMemory();
                }
-               PyMem_RESIZE(item, char, (Py_SIZE(self) + n) * itemsize);
-               if (item == NULL) {
-                       PyErr_NoMemory();
+               if (array_resize(self, old_size + n) == -1)
                        return NULL;
-               }
-               self->ob_item = item;
-               Py_SIZE(self) += n;
-               self->allocated = Py_SIZE(self);
-               memcpy(item + (Py_SIZE(self) - n) * itemsize,
-                      str, itemsize*n);
+               memcpy(self->ob_item + old_size * itemsize,
+                       str, n * itemsize);
        }
        Py_INCREF(Py_None);
        return Py_None;
@@ -1458,19 +1417,10 @@ array_fromunicode(arrayobject *self, PyObject *args)
                return NULL;
        }
        if (n > 0) {
-               Py_UNICODE *item = (Py_UNICODE *) self->ob_item;
-               if (Py_SIZE(self) > PY_SSIZE_T_MAX - n) {
-                       return PyErr_NoMemory();
-               }
-               PyMem_RESIZE(item, Py_UNICODE, Py_SIZE(self) + n);
-               if (item == NULL) {
-                       PyErr_NoMemory();
+               Py_ssize_t old_size = Py_SIZE(self);
+               if (array_resize(self, old_size + n) == -1)
                        return NULL;
-               }
-               self->ob_item = (char *) item;
-               Py_SIZE(self) += n;
-               self->allocated = Py_SIZE(self);
-               memcpy(item + Py_SIZE(self) - n,
+               memcpy(self->ob_item + old_size * sizeof(Py_UNICODE),
                       ustr, n * sizeof(Py_UNICODE));
        }
 
@@ -1740,12 +1690,12 @@ array_ass_subscr(arrayobject* self, PyObject* item, PyObject* value)
                                self->ob_item + stop * itemsize,
                                (Py_SIZE(self) - stop) * itemsize);
                        if (array_resize(self, Py_SIZE(self) +
-                                        needed - slicelength) < 0)
+                               needed - slicelength) < 0)
                                return -1;
                }
                else if (slicelength < needed) {
                        if (array_resize(self, Py_SIZE(self) +
-                                        needed - slicelength) < 0)
+                               needed - slicelength) < 0)
                                return -1;
                        memmove(self->ob_item + (start + needed) * itemsize,
                                self->ob_item + stop * itemsize,