Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit bc42040

Browse files
committed
Issue #4569: Interpreter crash when mutating a memoryview with an item size larger than 1.
(together with a bit of reindenting)
1 parent f973407 commit bc42040

3 files changed

Lines changed: 117 additions & 93 deletions

File tree

Lib/test/test_memoryview.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,15 @@ def test_constructor(self):
207207
self.assertRaises(TypeError, memoryview, argument=ob)
208208
self.assertRaises(TypeError, memoryview, ob, argument=True)
209209

210+
def test_array_assign(self):
211+
# Issue #4569: segfault when mutating a memoryview with itemsize != 1
212+
from array import array
213+
a = array('i', range(10))
214+
m = memoryview(a)
215+
new_a = array('i', range(9, -1, -1))
216+
m[:] = new_a
217+
self.assertEquals(a, new_a)
218+
210219

211220
class MemorySliceTest(unittest.TestCase, CommonMemoryTests):
212221
base_object = b"XabcdefY"

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ Core and Builtins
2929
kept open but the file object behaves like a closed file. The ``FileIO``
3030
object also got a new readonly attribute ``closefd``.
3131

32+
- Issue #4569: Interpreter crash when mutating a memoryview with an item size
33+
larger than 1.
34+
3235
Library
3336
-------
3437

Objects/memoryobject.c

Lines changed: 105 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,18 @@ dup_buffer(Py_buffer *dest, Py_buffer *src)
1313
dest->strides = &(dest->itemsize);
1414
}
1515

16+
/* XXX The buffer API should mandate that the shape array be non-NULL, but
17+
it would complicate some code since the (de)allocation semantics of shape
18+
are not specified. */
19+
static Py_ssize_t
20+
get_shape0(Py_buffer *buf)
21+
{
22+
if (buf->shape != NULL)
23+
return buf->shape[0];
24+
assert(buf->ndim == 1 && buf->itemsize > 0);
25+
return buf->len / buf->itemsize;
26+
}
27+
1628
static int
1729
memory_getbuf(PyMemoryViewObject *self, Py_buffer *view, int flags)
1830
{
@@ -523,99 +535,99 @@ memory_length(PyMemoryViewObject *self)
523535
static PyObject *
524536
memory_subscript(PyMemoryViewObject *self, PyObject *key)
525537
{
526-
Py_buffer *view;
527-
view = &(self->view);
528-
529-
if (view->ndim == 0) {
530-
if (key == Py_Ellipsis ||
538+
Py_buffer *view;
539+
view = &(self->view);
540+
541+
if (view->ndim == 0) {
542+
if (key == Py_Ellipsis ||
531543
(PyTuple_Check(key) && PyTuple_GET_SIZE(key)==0)) {
532-
Py_INCREF(self);
533-
return (PyObject *)self;
534-
}
535-
else {
536-
PyErr_SetString(PyExc_IndexError,
544+
Py_INCREF(self);
545+
return (PyObject *)self;
546+
}
547+
else {
548+
PyErr_SetString(PyExc_IndexError,
537549
"invalid indexing of 0-dim memory");
538-
return NULL;
539-
}
540-
}
541-
if (PyIndex_Check(key)) {
542-
Py_ssize_t result;
543-
result = PyNumber_AsSsize_t(key, NULL);
544-
if (result == -1 && PyErr_Occurred())
545-
return NULL;
546-
if (view->ndim == 1) {
547-
/* Return a bytes object */
548-
char *ptr;
549-
ptr = (char *)view->buf;
550-
if (result < 0) {
551-
result += view->shape[0];
552-
}
553-
if ((result < 0) || (result >= view->shape[0])) {
554-
PyErr_SetString(PyExc_IndexError,
555-
"index out of bounds");
556-
return NULL;
557-
}
558-
if (view->strides == NULL)
559-
ptr += view->itemsize * result;
560-
else
561-
ptr += view->strides[0] * result;
562-
if (view->suboffsets != NULL &&
550+
return NULL;
551+
}
552+
}
553+
if (PyIndex_Check(key)) {
554+
Py_ssize_t result;
555+
result = PyNumber_AsSsize_t(key, NULL);
556+
if (result == -1 && PyErr_Occurred())
557+
return NULL;
558+
if (view->ndim == 1) {
559+
/* Return a bytes object */
560+
char *ptr;
561+
ptr = (char *)view->buf;
562+
if (result < 0) {
563+
result += get_shape0(view);
564+
}
565+
if ((result < 0) || (result >= get_shape0(view))) {
566+
PyErr_SetString(PyExc_IndexError,
567+
"index out of bounds");
568+
return NULL;
569+
}
570+
if (view->strides == NULL)
571+
ptr += view->itemsize * result;
572+
else
573+
ptr += view->strides[0] * result;
574+
if (view->suboffsets != NULL &&
563575
view->suboffsets[0] >= 0)
564576
{
565-
ptr = *((char **)ptr) + view->suboffsets[0];
566-
}
567-
return PyBytes_FromStringAndSize(ptr, view->itemsize);
568-
}
569-
else {
570-
/* Return a new memory-view object */
571-
Py_buffer newview;
572-
memset(&newview, 0, sizeof(newview));
573-
/* XXX: This needs to be fixed so it
577+
ptr = *((char **)ptr) + view->suboffsets[0];
578+
}
579+
return PyBytes_FromStringAndSize(ptr, view->itemsize);
580+
}
581+
else {
582+
/* Return a new memory-view object */
583+
Py_buffer newview;
584+
memset(&newview, 0, sizeof(newview));
585+
/* XXX: This needs to be fixed so it
574586
actually returns a sub-view
575-
*/
576-
return PyMemoryView_FromBuffer(&newview);
577-
}
578-
}
579-
else if (PySlice_Check(key)) {
580-
Py_ssize_t start, stop, step, slicelength;
581-
582-
if (PySlice_GetIndicesEx((PySliceObject*)key, view->len,
583-
&start, &stop, &step, &slicelength) < 0) {
584-
return NULL;
585-
}
586-
587-
if (step == 1 && view->ndim == 1) {
588-
Py_buffer newview;
589-
void *newbuf = (char *) view->buf
590-
+ start * view->itemsize;
591-
int newflags = view->readonly
592-
? PyBUF_CONTIG_RO : PyBUF_CONTIG;
593-
594-
/* XXX There should be an API to create a subbuffer */
595-
if (view->obj != NULL) {
596-
if (PyObject_GetBuffer(view->obj,
597-
&newview, newflags) == -1)
598-
return NULL;
599-
}
600-
else {
601-
newview = *view;
602-
}
603-
newview.buf = newbuf;
604-
newview.len = slicelength;
605-
newview.format = view->format;
606-
if (view->shape == &(view->len))
607-
newview.shape = &(newview.len);
608-
if (view->strides == &(view->itemsize))
609-
newview.strides = &(newview.itemsize);
610-
return PyMemoryView_FromBuffer(&newview);
611-
}
612-
PyErr_SetNone(PyExc_NotImplementedError);
613-
return NULL;
614-
}
615-
PyErr_Format(PyExc_TypeError,
616-
"cannot index memory using \"%.200s\"",
617-
key->ob_type->tp_name);
618-
return NULL;
587+
*/
588+
return PyMemoryView_FromBuffer(&newview);
589+
}
590+
}
591+
else if (PySlice_Check(key)) {
592+
Py_ssize_t start, stop, step, slicelength;
593+
594+
if (PySlice_GetIndicesEx((PySliceObject*)key, get_shape0(view),
595+
&start, &stop, &step, &slicelength) < 0) {
596+
return NULL;
597+
}
598+
599+
if (step == 1 && view->ndim == 1) {
600+
Py_buffer newview;
601+
void *newbuf = (char *) view->buf
602+
+ start * view->itemsize;
603+
int newflags = view->readonly
604+
? PyBUF_CONTIG_RO : PyBUF_CONTIG;
605+
606+
/* XXX There should be an API to create a subbuffer */
607+
if (view->obj != NULL) {
608+
if (PyObject_GetBuffer(view->obj,
609+
&newview, newflags) == -1)
610+
return NULL;
611+
}
612+
else {
613+
newview = *view;
614+
}
615+
newview.buf = newbuf;
616+
newview.len = slicelength;
617+
newview.format = view->format;
618+
if (view->shape == &(view->len))
619+
newview.shape = &(newview.len);
620+
if (view->strides == &(view->itemsize))
621+
newview.strides = &(newview.itemsize);
622+
return PyMemoryView_FromBuffer(&newview);
623+
}
624+
PyErr_SetNone(PyExc_NotImplementedError);
625+
return NULL;
626+
}
627+
PyErr_Format(PyExc_TypeError,
628+
"cannot index memory using \"%.200s\"",
629+
key->ob_type->tp_name);
630+
return NULL;
619631
}
620632

621633

@@ -642,9 +654,9 @@ memory_ass_sub(PyMemoryViewObject *self, PyObject *key, PyObject *value)
642654
if (start == -1 && PyErr_Occurred())
643655
return -1;
644656
if (start < 0) {
645-
start += view->shape[0];
657+
start += get_shape0(view);
646658
}
647-
if ((start < 0) || (start >= view->shape[0])) {
659+
if ((start < 0) || (start >= get_shape0(view))) {
648660
PyErr_SetString(PyExc_IndexError,
649661
"index out of bounds");
650662
return -1;
@@ -654,7 +666,7 @@ memory_ass_sub(PyMemoryViewObject *self, PyObject *key, PyObject *value)
654666
else if (PySlice_Check(key)) {
655667
Py_ssize_t stop, step;
656668

657-
if (PySlice_GetIndicesEx((PySliceObject*)key, view->len,
669+
if (PySlice_GetIndicesEx((PySliceObject*)key, get_shape0(view),
658670
&start, &stop, &step, &len) < 0) {
659671
return -1;
660672
}
@@ -681,15 +693,15 @@ memory_ass_sub(PyMemoryViewObject *self, PyObject *key, PyObject *value)
681693
view->obj->ob_type->tp_name, srcview.obj->ob_type->tp_name);
682694
goto _error;
683695
}
684-
if (srcview.len != len) {
696+
bytelen = len * view->itemsize;
697+
if (bytelen != srcview.len) {
685698
PyErr_SetString(PyExc_ValueError,
686699
"cannot modify size of memoryview object");
687700
goto _error;
688701
}
689702
/* Do the actual copy */
690703
destbuf = (char *) view->buf + start * view->itemsize;
691704
srcbuf = (char *) srcview.buf;
692-
bytelen = len * view->itemsize;
693705
if (destbuf + bytelen < srcbuf || srcbuf + bytelen < destbuf)
694706
/* No overlapping */
695707
memcpy(destbuf, srcbuf, bytelen);

0 commit comments

Comments
 (0)