Commit bc420400 authored by Antoine Pitrou's avatar Antoine Pitrou

Issue #4569: Interpreter crash when mutating a memoryview with an item size larger than 1.

(together with a bit of reindenting)
parent f9734076
...@@ -207,6 +207,15 @@ class MemoryviewTest(unittest.TestCase, CommonMemoryTests): ...@@ -207,6 +207,15 @@ class MemoryviewTest(unittest.TestCase, CommonMemoryTests):
self.assertRaises(TypeError, memoryview, argument=ob) self.assertRaises(TypeError, memoryview, argument=ob)
self.assertRaises(TypeError, memoryview, ob, argument=True) self.assertRaises(TypeError, memoryview, ob, argument=True)
def test_array_assign(self):
# Issue #4569: segfault when mutating a memoryview with itemsize != 1
from array import array
a = array('i', range(10))
m = memoryview(a)
new_a = array('i', range(9, -1, -1))
m[:] = new_a
self.assertEquals(a, new_a)
class MemorySliceTest(unittest.TestCase, CommonMemoryTests): class MemorySliceTest(unittest.TestCase, CommonMemoryTests):
base_object = b"XabcdefY" base_object = b"XabcdefY"
......
...@@ -29,6 +29,9 @@ Core and Builtins ...@@ -29,6 +29,9 @@ Core and Builtins
kept open but the file object behaves like a closed file. The ``FileIO`` kept open but the file object behaves like a closed file. The ``FileIO``
object also got a new readonly attribute ``closefd``. object also got a new readonly attribute ``closefd``.
- Issue #4569: Interpreter crash when mutating a memoryview with an item size
larger than 1.
Library Library
------- -------
......
...@@ -13,6 +13,18 @@ dup_buffer(Py_buffer *dest, Py_buffer *src) ...@@ -13,6 +13,18 @@ dup_buffer(Py_buffer *dest, Py_buffer *src)
dest->strides = &(dest->itemsize); dest->strides = &(dest->itemsize);
} }
/* XXX The buffer API should mandate that the shape array be non-NULL, but
it would complicate some code since the (de)allocation semantics of shape
are not specified. */
static Py_ssize_t
get_shape0(Py_buffer *buf)
{
if (buf->shape != NULL)
return buf->shape[0];
assert(buf->ndim == 1 && buf->itemsize > 0);
return buf->len / buf->itemsize;
}
static int static int
memory_getbuf(PyMemoryViewObject *self, Py_buffer *view, int flags) memory_getbuf(PyMemoryViewObject *self, Py_buffer *view, int flags)
{ {
...@@ -523,99 +535,99 @@ memory_length(PyMemoryViewObject *self) ...@@ -523,99 +535,99 @@ memory_length(PyMemoryViewObject *self)
static PyObject * static PyObject *
memory_subscript(PyMemoryViewObject *self, PyObject *key) memory_subscript(PyMemoryViewObject *self, PyObject *key)
{ {
Py_buffer *view; Py_buffer *view;
view = &(self->view); view = &(self->view);
if (view->ndim == 0) { if (view->ndim == 0) {
if (key == Py_Ellipsis || if (key == Py_Ellipsis ||
(PyTuple_Check(key) && PyTuple_GET_SIZE(key)==0)) { (PyTuple_Check(key) && PyTuple_GET_SIZE(key)==0)) {
Py_INCREF(self); Py_INCREF(self);
return (PyObject *)self; return (PyObject *)self;
} }
else { else {
PyErr_SetString(PyExc_IndexError, PyErr_SetString(PyExc_IndexError,
"invalid indexing of 0-dim memory"); "invalid indexing of 0-dim memory");
return NULL; return NULL;
} }
} }
if (PyIndex_Check(key)) { if (PyIndex_Check(key)) {
Py_ssize_t result; Py_ssize_t result;
result = PyNumber_AsSsize_t(key, NULL); result = PyNumber_AsSsize_t(key, NULL);
if (result == -1 && PyErr_Occurred()) if (result == -1 && PyErr_Occurred())
return NULL; return NULL;
if (view->ndim == 1) { if (view->ndim == 1) {
/* Return a bytes object */ /* Return a bytes object */
char *ptr; char *ptr;
ptr = (char *)view->buf; ptr = (char *)view->buf;
if (result < 0) { if (result < 0) {
result += view->shape[0]; result += get_shape0(view);
} }
if ((result < 0) || (result >= view->shape[0])) { if ((result < 0) || (result >= get_shape0(view))) {
PyErr_SetString(PyExc_IndexError, PyErr_SetString(PyExc_IndexError,
"index out of bounds"); "index out of bounds");
return NULL; return NULL;
} }
if (view->strides == NULL) if (view->strides == NULL)
ptr += view->itemsize * result; ptr += view->itemsize * result;
else else
ptr += view->strides[0] * result; ptr += view->strides[0] * result;
if (view->suboffsets != NULL && if (view->suboffsets != NULL &&
view->suboffsets[0] >= 0) view->suboffsets[0] >= 0)
{ {
ptr = *((char **)ptr) + view->suboffsets[0]; ptr = *((char **)ptr) + view->suboffsets[0];
} }
return PyBytes_FromStringAndSize(ptr, view->itemsize); return PyBytes_FromStringAndSize(ptr, view->itemsize);
} }
else { else {
/* Return a new memory-view object */ /* Return a new memory-view object */
Py_buffer newview; Py_buffer newview;
memset(&newview, 0, sizeof(newview)); memset(&newview, 0, sizeof(newview));
/* XXX: This needs to be fixed so it /* XXX: This needs to be fixed so it
actually returns a sub-view actually returns a sub-view
*/ */
return PyMemoryView_FromBuffer(&newview); return PyMemoryView_FromBuffer(&newview);
} }
} }
else if (PySlice_Check(key)) { else if (PySlice_Check(key)) {
Py_ssize_t start, stop, step, slicelength; Py_ssize_t start, stop, step, slicelength;
if (PySlice_GetIndicesEx((PySliceObject*)key, view->len, if (PySlice_GetIndicesEx((PySliceObject*)key, get_shape0(view),
&start, &stop, &step, &slicelength) < 0) { &start, &stop, &step, &slicelength) < 0) {
return NULL; return NULL;
} }
if (step == 1 && view->ndim == 1) { if (step == 1 && view->ndim == 1) {
Py_buffer newview; Py_buffer newview;
void *newbuf = (char *) view->buf void *newbuf = (char *) view->buf
+ start * view->itemsize; + start * view->itemsize;
int newflags = view->readonly int newflags = view->readonly
? PyBUF_CONTIG_RO : PyBUF_CONTIG; ? PyBUF_CONTIG_RO : PyBUF_CONTIG;
/* XXX There should be an API to create a subbuffer */ /* XXX There should be an API to create a subbuffer */
if (view->obj != NULL) { if (view->obj != NULL) {
if (PyObject_GetBuffer(view->obj, if (PyObject_GetBuffer(view->obj,
&newview, newflags) == -1) &newview, newflags) == -1)
return NULL; return NULL;
} }
else { else {
newview = *view; newview = *view;
} }
newview.buf = newbuf; newview.buf = newbuf;
newview.len = slicelength; newview.len = slicelength;
newview.format = view->format; newview.format = view->format;
if (view->shape == &(view->len)) if (view->shape == &(view->len))
newview.shape = &(newview.len); newview.shape = &(newview.len);
if (view->strides == &(view->itemsize)) if (view->strides == &(view->itemsize))
newview.strides = &(newview.itemsize); newview.strides = &(newview.itemsize);
return PyMemoryView_FromBuffer(&newview); return PyMemoryView_FromBuffer(&newview);
} }
PyErr_SetNone(PyExc_NotImplementedError); PyErr_SetNone(PyExc_NotImplementedError);
return NULL; return NULL;
} }
PyErr_Format(PyExc_TypeError, PyErr_Format(PyExc_TypeError,
"cannot index memory using \"%.200s\"", "cannot index memory using \"%.200s\"",
key->ob_type->tp_name); key->ob_type->tp_name);
return NULL; return NULL;
} }
...@@ -642,9 +654,9 @@ memory_ass_sub(PyMemoryViewObject *self, PyObject *key, PyObject *value) ...@@ -642,9 +654,9 @@ memory_ass_sub(PyMemoryViewObject *self, PyObject *key, PyObject *value)
if (start == -1 && PyErr_Occurred()) if (start == -1 && PyErr_Occurred())
return -1; return -1;
if (start < 0) { if (start < 0) {
start += view->shape[0]; start += get_shape0(view);
} }
if ((start < 0) || (start >= view->shape[0])) { if ((start < 0) || (start >= get_shape0(view))) {
PyErr_SetString(PyExc_IndexError, PyErr_SetString(PyExc_IndexError,
"index out of bounds"); "index out of bounds");
return -1; return -1;
...@@ -654,7 +666,7 @@ memory_ass_sub(PyMemoryViewObject *self, PyObject *key, PyObject *value) ...@@ -654,7 +666,7 @@ memory_ass_sub(PyMemoryViewObject *self, PyObject *key, PyObject *value)
else if (PySlice_Check(key)) { else if (PySlice_Check(key)) {
Py_ssize_t stop, step; Py_ssize_t stop, step;
if (PySlice_GetIndicesEx((PySliceObject*)key, view->len, if (PySlice_GetIndicesEx((PySliceObject*)key, get_shape0(view),
&start, &stop, &step, &len) < 0) { &start, &stop, &step, &len) < 0) {
return -1; return -1;
} }
...@@ -681,7 +693,8 @@ memory_ass_sub(PyMemoryViewObject *self, PyObject *key, PyObject *value) ...@@ -681,7 +693,8 @@ memory_ass_sub(PyMemoryViewObject *self, PyObject *key, PyObject *value)
view->obj->ob_type->tp_name, srcview.obj->ob_type->tp_name); view->obj->ob_type->tp_name, srcview.obj->ob_type->tp_name);
goto _error; goto _error;
} }
if (srcview.len != len) { bytelen = len * view->itemsize;
if (bytelen != srcview.len) {
PyErr_SetString(PyExc_ValueError, PyErr_SetString(PyExc_ValueError,
"cannot modify size of memoryview object"); "cannot modify size of memoryview object");
goto _error; goto _error;
...@@ -689,7 +702,6 @@ memory_ass_sub(PyMemoryViewObject *self, PyObject *key, PyObject *value) ...@@ -689,7 +702,6 @@ memory_ass_sub(PyMemoryViewObject *self, PyObject *key, PyObject *value)
/* Do the actual copy */ /* Do the actual copy */
destbuf = (char *) view->buf + start * view->itemsize; destbuf = (char *) view->buf + start * view->itemsize;
srcbuf = (char *) srcview.buf; srcbuf = (char *) srcview.buf;
bytelen = len * view->itemsize;
if (destbuf + bytelen < srcbuf || srcbuf + bytelen < destbuf) if (destbuf + bytelen < srcbuf || srcbuf + bytelen < destbuf)
/* No overlapping */ /* No overlapping */
memcpy(destbuf, srcbuf, bytelen); memcpy(destbuf, srcbuf, bytelen);
......
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