Issue #4509: bugs in bytearray with exports (buffer protocol)

This commit is contained in:
Antoine Pitrou 2008-12-06 21:27:53 +00:00
parent e6d4a9bdbc
commit 5504e893f8
3 changed files with 65 additions and 11 deletions

View File

@ -769,6 +769,37 @@ class ByteArrayTest(BaseBytesTest):
self.assertEqual(b, b"") self.assertEqual(b, b"")
self.assertEqual(c, 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): class AssortedBytesTest(unittest.TestCase):
# #

View File

@ -21,6 +21,9 @@ Core and Builtins
growing read buffer. Fixed by using the same growth rate algorithm as growing read buffer. Fixed by using the same growth rate algorithm as
Python 2.x. Python 2.x.
- Issue #4509: Various issues surrounding resize of bytearray objects to
which there are buffer exports (e.g. memoryview instances).
Library Library
------- -------

View File

@ -100,6 +100,17 @@ _getbuffer(PyObject *obj, Py_buffer *view)
return view->len; 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 */ /* Direct API functions */
PyObject * PyObject *
@ -180,6 +191,13 @@ PyByteArray_Resize(PyObject *self, Py_ssize_t size)
assert(PyByteArray_Check(self)); assert(PyByteArray_Check(self));
assert(size >= 0); assert(size >= 0);
if (size == Py_SIZE(self)) {
return 0;
}
if (!_canresize((PyByteArrayObject *)self)) {
return -1;
}
if (size < alloc / 2) { if (size < alloc / 2) {
/* Major downsize; resize down to exact size */ /* Major downsize; resize down to exact size */
alloc = size + 1; alloc = size + 1;
@ -199,16 +217,6 @@ PyByteArray_Resize(PyObject *self, Py_ssize_t size)
alloc = size + 1; 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); sval = PyMem_Realloc(((PyByteArrayObject *)self)->ob_bytes, alloc);
if (sval == NULL) { if (sval == NULL) {
PyErr_NoMemory(); 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 (avail > needed) { if (avail > needed) {
if (!_canresize(self)) {
res = -1;
goto finish;
}
/* /*
0 lo hi old_size 0 lo hi old_size
| |<----avail----->|<-----tomove------>| | |<----avail----->|<-----tomove------>|
@ -605,6 +617,8 @@ bytes_ass_subscript(PyByteArrayObject *self, PyObject *index, PyObject *values)
stop = start; stop = start;
if (step == 1) { if (step == 1) {
if (slicelen != needed) { if (slicelen != needed) {
if (!_canresize(self))
return -1;
if (slicelen > needed) { if (slicelen > needed) {
/* /*
0 start stop old_size 0 start stop old_size
@ -640,6 +654,8 @@ bytes_ass_subscript(PyByteArrayObject *self, PyObject *index, PyObject *values)
/* Delete slice */ /* Delete slice */
Py_ssize_t cur, i; Py_ssize_t cur, i;
if (!_canresize(self))
return -1;
if (step < 0) { if (step < 0) {
stop = start + 1; stop = start + 1;
start = stop + step * (slicelen - 1) - 1; start = stop + step * (slicelen - 1) - 1;
@ -2659,6 +2675,8 @@ bytes_pop(PyByteArrayObject *self, PyObject *args)
PyErr_SetString(PyExc_IndexError, "pop index out of range"); PyErr_SetString(PyExc_IndexError, "pop index out of range");
return NULL; return NULL;
} }
if (!_canresize(self))
return NULL;
value = self->ob_bytes[where]; value = self->ob_bytes[where];
memmove(self->ob_bytes + where, self->ob_bytes + where + 1, n - 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"); PyErr_SetString(PyExc_ValueError, "value not found in bytes");
return NULL; return NULL;
} }
if (!_canresize(self))
return NULL;
memmove(self->ob_bytes + where, self->ob_bytes + where + 1, n - where); memmove(self->ob_bytes + where, self->ob_bytes + where + 1, n - where);
if (PyByteArray_Resize((PyObject *)self, n - 1) < 0) if (PyByteArray_Resize((PyObject *)self, n - 1) < 0)