From 5504e893f84f07d5930094f07bcd479d859b5f0c Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Sat, 6 Dec 2008 21:27:53 +0000 Subject: Issue #4509: bugs in bytearray with exports (buffer protocol) --- Lib/test/test_bytes.py | 31 +++++++++++++++++++++++++++++++ Misc/NEWS | 3 +++ Objects/bytearrayobject.c | 42 +++++++++++++++++++++++++++++++----------- 3 files changed, 65 insertions(+), 11 deletions(-) diff --git a/Lib/test/test_bytes.py b/Lib/test/test_bytes.py index 24812a5..b1427b5 100644 --- a/Lib/test/test_bytes.py +++ b/Lib/test/test_bytes.py @@ -769,6 +769,37 @@ class ByteArrayTest(BaseBytesTest): self.assertEqual(b, b"") self.assertEqual(c, b"") + def test_resize_forbidden(self): + # #4509: can't resize a bytearray when there are buffer exports, even + # if it wouldn't reallocate the underlying buffer. + # Furthermore, no destructive changes to the buffer may be applied + # before raising the error. + b = bytearray(range(10)) + v = memoryview(b) + def resize(n): + b[1:-1] = range(n + 1, 2*n - 1) + resize(10) + orig = b[:] + self.assertRaises(BufferError, resize, 11) + self.assertEquals(b, orig) + self.assertRaises(BufferError, resize, 9) + self.assertEquals(b, orig) + self.assertRaises(BufferError, resize, 0) + self.assertEquals(b, orig) + # Other operations implying resize + self.assertRaises(BufferError, b.pop, 0) + self.assertEquals(b, orig) + self.assertRaises(BufferError, b.remove, b[1]) + self.assertEquals(b, orig) + def delitem(): + del b[1] + self.assertRaises(BufferError, delitem) + self.assertEquals(b, orig) + # deleting a non-contiguous slice + def delslice(): + b[1:-1:2] = b"" + self.assertRaises(BufferError, delslice) + self.assertEquals(b, orig) class AssortedBytesTest(unittest.TestCase): # diff --git a/Misc/NEWS b/Misc/NEWS index 9445457..81b0900 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -21,6 +21,9 @@ Core and Builtins growing read buffer. Fixed by using the same growth rate algorithm as Python 2.x. +- Issue #4509: Various issues surrounding resize of bytearray objects to + which there are buffer exports (e.g. memoryview instances). + Library ------- diff --git a/Objects/bytearrayobject.c b/Objects/bytearrayobject.c index 707c844..1620652 100644 --- a/Objects/bytearrayobject.c +++ b/Objects/bytearrayobject.c @@ -100,6 +100,17 @@ _getbuffer(PyObject *obj, Py_buffer *view) return view->len; } +static int +_canresize(PyByteArrayObject *self) +{ + if (self->ob_exports > 0) { + PyErr_SetString(PyExc_BufferError, + "Existing exports of data: object cannot be re-sized"); + return 0; + } + return 1; +} + /* Direct API functions */ PyObject * @@ -180,6 +191,13 @@ PyByteArray_Resize(PyObject *self, Py_ssize_t size) assert(PyByteArray_Check(self)); assert(size >= 0); + if (size == Py_SIZE(self)) { + return 0; + } + if (!_canresize((PyByteArrayObject *)self)) { + return -1; + } + if (size < alloc / 2) { /* Major downsize; resize down to exact size */ alloc = size + 1; @@ -199,16 +217,6 @@ PyByteArray_Resize(PyObject *self, Py_ssize_t size) alloc = size + 1; } - if (((PyByteArrayObject *)self)->ob_exports > 0) { - /* - fprintf(stderr, "%d: %s", ((PyByteArrayObject *)self)->ob_exports, - ((PyByteArrayObject *)self)->ob_bytes); - */ - PyErr_SetString(PyExc_BufferError, - "Existing exports of data: object cannot be re-sized"); - return -1; - } - sval = PyMem_Realloc(((PyByteArrayObject *)self)->ob_bytes, alloc); if (sval == NULL) { PyErr_NoMemory(); @@ -473,6 +481,10 @@ bytes_setslice(PyByteArrayObject *self, Py_ssize_t lo, Py_ssize_t hi, if (avail != needed) { if (avail > needed) { + if (!_canresize(self)) { + res = -1; + goto finish; + } /* 0 lo hi old_size | |<----avail----->|<-----tomove------>| @@ -605,6 +617,8 @@ bytes_ass_subscript(PyByteArrayObject *self, PyObject *index, PyObject *values) stop = start; if (step == 1) { if (slicelen != needed) { + if (!_canresize(self)) + return -1; if (slicelen > needed) { /* 0 start stop old_size @@ -640,6 +654,8 @@ bytes_ass_subscript(PyByteArrayObject *self, PyObject *index, PyObject *values) /* Delete slice */ Py_ssize_t cur, i; + if (!_canresize(self)) + return -1; if (step < 0) { stop = start + 1; start = stop + step * (slicelen - 1) - 1; @@ -1401,7 +1417,7 @@ bytes_translate(PyByteArrayObject *self, PyObject *args) } goto done; } - + for (i = 0; i < 256; i++) trans_table[i] = Py_CHARMASK(table[i]); @@ -2659,6 +2675,8 @@ bytes_pop(PyByteArrayObject *self, PyObject *args) PyErr_SetString(PyExc_IndexError, "pop index out of range"); return NULL; } + if (!_canresize(self)) + return NULL; value = self->ob_bytes[where]; memmove(self->ob_bytes + where, self->ob_bytes + where + 1, n - where); @@ -2689,6 +2707,8 @@ bytes_remove(PyByteArrayObject *self, PyObject *arg) PyErr_SetString(PyExc_ValueError, "value not found in bytes"); return NULL; } + if (!_canresize(self)) + return NULL; memmove(self->ob_bytes + where, self->ob_bytes + where + 1, n - where); if (PyByteArray_Resize((PyObject *)self, n - 1) < 0) -- cgit v0.12