mirror of https://github.com/python/cpython
gh-92888: Fix memoryview bad `__index__` use after free (GH-92946)
Co-authored-by: chilaxan <35645806+chilaxan@users.noreply.github.com> Co-authored-by: Serhiy Storchaka <3659035+serhiy-storchaka@users.noreply.github.com>
This commit is contained in:
parent
a51742ab82
commit
11190c4ad0
|
@ -545,6 +545,107 @@ class OtherTest(unittest.TestCase):
|
||||||
with self.assertRaises(TypeError):
|
with self.assertRaises(TypeError):
|
||||||
pickle.dumps(m, proto)
|
pickle.dumps(m, proto)
|
||||||
|
|
||||||
|
def test_use_released_memory(self):
|
||||||
|
# gh-92888: Previously it was possible to use a memoryview even after
|
||||||
|
# backing buffer is freed in certain cases. This tests that those
|
||||||
|
# cases raise an exception.
|
||||||
|
size = 128
|
||||||
|
def release():
|
||||||
|
m.release()
|
||||||
|
nonlocal ba
|
||||||
|
ba = bytearray(size)
|
||||||
|
class MyIndex:
|
||||||
|
def __index__(self):
|
||||||
|
release()
|
||||||
|
return 4
|
||||||
|
class MyFloat:
|
||||||
|
def __float__(self):
|
||||||
|
release()
|
||||||
|
return 4.25
|
||||||
|
class MyBool:
|
||||||
|
def __bool__(self):
|
||||||
|
release()
|
||||||
|
return True
|
||||||
|
|
||||||
|
ba = None
|
||||||
|
m = memoryview(bytearray(b'\xff'*size))
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
m[MyIndex()]
|
||||||
|
|
||||||
|
ba = None
|
||||||
|
m = memoryview(bytearray(b'\xff'*size))
|
||||||
|
self.assertEqual(list(m[:MyIndex()]), [255] * 4)
|
||||||
|
|
||||||
|
ba = None
|
||||||
|
m = memoryview(bytearray(b'\xff'*size))
|
||||||
|
self.assertEqual(list(m[MyIndex():8]), [255] * 4)
|
||||||
|
|
||||||
|
ba = None
|
||||||
|
m = memoryview(bytearray(b'\xff'*size)).cast('B', (64, 2))
|
||||||
|
with self.assertRaisesRegex(ValueError, "operation forbidden"):
|
||||||
|
m[MyIndex(), 0]
|
||||||
|
|
||||||
|
ba = None
|
||||||
|
m = memoryview(bytearray(b'\xff'*size)).cast('B', (2, 64))
|
||||||
|
with self.assertRaisesRegex(ValueError, "operation forbidden"):
|
||||||
|
m[0, MyIndex()]
|
||||||
|
|
||||||
|
ba = None
|
||||||
|
m = memoryview(bytearray(b'\xff'*size))
|
||||||
|
with self.assertRaisesRegex(ValueError, "operation forbidden"):
|
||||||
|
m[MyIndex()] = 42
|
||||||
|
self.assertEqual(ba[:8], b'\0'*8)
|
||||||
|
|
||||||
|
ba = None
|
||||||
|
m = memoryview(bytearray(b'\xff'*size))
|
||||||
|
with self.assertRaisesRegex(ValueError, "operation forbidden"):
|
||||||
|
m[:MyIndex()] = b'spam'
|
||||||
|
self.assertEqual(ba[:8], b'\0'*8)
|
||||||
|
|
||||||
|
ba = None
|
||||||
|
m = memoryview(bytearray(b'\xff'*size))
|
||||||
|
with self.assertRaisesRegex(ValueError, "operation forbidden"):
|
||||||
|
m[MyIndex():8] = b'spam'
|
||||||
|
self.assertEqual(ba[:8], b'\0'*8)
|
||||||
|
|
||||||
|
ba = None
|
||||||
|
m = memoryview(bytearray(b'\xff'*size)).cast('B', (64, 2))
|
||||||
|
with self.assertRaisesRegex(ValueError, "operation forbidden"):
|
||||||
|
m[MyIndex(), 0] = 42
|
||||||
|
self.assertEqual(ba[8:16], b'\0'*8)
|
||||||
|
ba = None
|
||||||
|
m = memoryview(bytearray(b'\xff'*size)).cast('B', (2, 64))
|
||||||
|
with self.assertRaisesRegex(ValueError, "operation forbidden"):
|
||||||
|
m[0, MyIndex()] = 42
|
||||||
|
self.assertEqual(ba[:8], b'\0'*8)
|
||||||
|
|
||||||
|
ba = None
|
||||||
|
m = memoryview(bytearray(b'\xff'*size))
|
||||||
|
with self.assertRaisesRegex(ValueError, "operation forbidden"):
|
||||||
|
m[0] = MyIndex()
|
||||||
|
self.assertEqual(ba[:8], b'\0'*8)
|
||||||
|
|
||||||
|
for fmt in 'bhilqnBHILQN':
|
||||||
|
with self.subTest(fmt=fmt):
|
||||||
|
ba = None
|
||||||
|
m = memoryview(bytearray(b'\xff'*size)).cast(fmt)
|
||||||
|
with self.assertRaisesRegex(ValueError, "operation forbidden"):
|
||||||
|
m[0] = MyIndex()
|
||||||
|
self.assertEqual(ba[:8], b'\0'*8)
|
||||||
|
|
||||||
|
for fmt in 'fd':
|
||||||
|
with self.subTest(fmt=fmt):
|
||||||
|
ba = None
|
||||||
|
m = memoryview(bytearray(b'\xff'*size)).cast(fmt)
|
||||||
|
with self.assertRaisesRegex(ValueError, "operation forbidden"):
|
||||||
|
m[0] = MyFloat()
|
||||||
|
self.assertEqual(ba[:8], b'\0'*8)
|
||||||
|
|
||||||
|
ba = None
|
||||||
|
m = memoryview(bytearray(b'\xff'*size)).cast('?')
|
||||||
|
with self.assertRaisesRegex(ValueError, "operation forbidden"):
|
||||||
|
m[0] = MyBool()
|
||||||
|
self.assertEqual(ba[:8], b'\0'*8)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Fix ``memoryview`` use after free when accessing the backing buffer in certain cases.
|
||||||
|
|
|
@ -193,6 +193,11 @@ PyTypeObject _PyManagedBuffer_Type = {
|
||||||
return -1; \
|
return -1; \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* See gh-92888. These macros signal that we need to check the memoryview
|
||||||
|
again due to possible read after frees. */
|
||||||
|
#define CHECK_RELEASED_AGAIN(mv) CHECK_RELEASED(mv)
|
||||||
|
#define CHECK_RELEASED_INT_AGAIN(mv) CHECK_RELEASED_INT(mv)
|
||||||
|
|
||||||
#define CHECK_LIST_OR_TUPLE(v) \
|
#define CHECK_LIST_OR_TUPLE(v) \
|
||||||
if (!PyList_Check(v) && !PyTuple_Check(v)) { \
|
if (!PyList_Check(v) && !PyTuple_Check(v)) { \
|
||||||
PyErr_SetString(PyExc_TypeError, \
|
PyErr_SetString(PyExc_TypeError, \
|
||||||
|
@ -381,8 +386,9 @@ copy_rec(const Py_ssize_t *shape, Py_ssize_t ndim, Py_ssize_t itemsize,
|
||||||
|
|
||||||
/* Faster copying of one-dimensional arrays. */
|
/* Faster copying of one-dimensional arrays. */
|
||||||
static int
|
static int
|
||||||
copy_single(const Py_buffer *dest, const Py_buffer *src)
|
copy_single(PyMemoryViewObject *self, const Py_buffer *dest, const Py_buffer *src)
|
||||||
{
|
{
|
||||||
|
CHECK_RELEASED_INT_AGAIN(self);
|
||||||
char *mem = NULL;
|
char *mem = NULL;
|
||||||
|
|
||||||
assert(dest->ndim == 1);
|
assert(dest->ndim == 1);
|
||||||
|
@ -1677,7 +1683,7 @@ pylong_as_zu(PyObject *item)
|
||||||
module syntax. This function is very sensitive to small changes. With this
|
module syntax. This function is very sensitive to small changes. With this
|
||||||
layout gcc automatically generates a fast jump table. */
|
layout gcc automatically generates a fast jump table. */
|
||||||
static inline PyObject *
|
static inline PyObject *
|
||||||
unpack_single(const char *ptr, const char *fmt)
|
unpack_single(PyMemoryViewObject *self, const char *ptr, const char *fmt)
|
||||||
{
|
{
|
||||||
unsigned long long llu;
|
unsigned long long llu;
|
||||||
unsigned long lu;
|
unsigned long lu;
|
||||||
|
@ -1689,6 +1695,8 @@ unpack_single(const char *ptr, const char *fmt)
|
||||||
unsigned char uc;
|
unsigned char uc;
|
||||||
void *p;
|
void *p;
|
||||||
|
|
||||||
|
CHECK_RELEASED_AGAIN(self);
|
||||||
|
|
||||||
switch (fmt[0]) {
|
switch (fmt[0]) {
|
||||||
|
|
||||||
/* signed integers and fast path for 'B' */
|
/* signed integers and fast path for 'B' */
|
||||||
|
@ -1767,7 +1775,7 @@ err_format:
|
||||||
/* Pack a single item. 'fmt' can be any native format character in
|
/* Pack a single item. 'fmt' can be any native format character in
|
||||||
struct module syntax. */
|
struct module syntax. */
|
||||||
static int
|
static int
|
||||||
pack_single(char *ptr, PyObject *item, const char *fmt)
|
pack_single(PyMemoryViewObject *self, char *ptr, PyObject *item, const char *fmt)
|
||||||
{
|
{
|
||||||
unsigned long long llu;
|
unsigned long long llu;
|
||||||
unsigned long lu;
|
unsigned long lu;
|
||||||
|
@ -1784,6 +1792,7 @@ pack_single(char *ptr, PyObject *item, const char *fmt)
|
||||||
ld = pylong_as_ld(item);
|
ld = pylong_as_ld(item);
|
||||||
if (ld == -1 && PyErr_Occurred())
|
if (ld == -1 && PyErr_Occurred())
|
||||||
goto err_occurred;
|
goto err_occurred;
|
||||||
|
CHECK_RELEASED_INT_AGAIN(self);
|
||||||
switch (fmt[0]) {
|
switch (fmt[0]) {
|
||||||
case 'b':
|
case 'b':
|
||||||
if (ld < SCHAR_MIN || ld > SCHAR_MAX) goto err_range;
|
if (ld < SCHAR_MIN || ld > SCHAR_MAX) goto err_range;
|
||||||
|
@ -1804,6 +1813,7 @@ pack_single(char *ptr, PyObject *item, const char *fmt)
|
||||||
lu = pylong_as_lu(item);
|
lu = pylong_as_lu(item);
|
||||||
if (lu == (unsigned long)-1 && PyErr_Occurred())
|
if (lu == (unsigned long)-1 && PyErr_Occurred())
|
||||||
goto err_occurred;
|
goto err_occurred;
|
||||||
|
CHECK_RELEASED_INT_AGAIN(self);
|
||||||
switch (fmt[0]) {
|
switch (fmt[0]) {
|
||||||
case 'B':
|
case 'B':
|
||||||
if (lu > UCHAR_MAX) goto err_range;
|
if (lu > UCHAR_MAX) goto err_range;
|
||||||
|
@ -1824,12 +1834,14 @@ pack_single(char *ptr, PyObject *item, const char *fmt)
|
||||||
lld = pylong_as_lld(item);
|
lld = pylong_as_lld(item);
|
||||||
if (lld == -1 && PyErr_Occurred())
|
if (lld == -1 && PyErr_Occurred())
|
||||||
goto err_occurred;
|
goto err_occurred;
|
||||||
|
CHECK_RELEASED_INT_AGAIN(self);
|
||||||
PACK_SINGLE(ptr, lld, long long);
|
PACK_SINGLE(ptr, lld, long long);
|
||||||
break;
|
break;
|
||||||
case 'Q':
|
case 'Q':
|
||||||
llu = pylong_as_llu(item);
|
llu = pylong_as_llu(item);
|
||||||
if (llu == (unsigned long long)-1 && PyErr_Occurred())
|
if (llu == (unsigned long long)-1 && PyErr_Occurred())
|
||||||
goto err_occurred;
|
goto err_occurred;
|
||||||
|
CHECK_RELEASED_INT_AGAIN(self);
|
||||||
PACK_SINGLE(ptr, llu, unsigned long long);
|
PACK_SINGLE(ptr, llu, unsigned long long);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -1838,12 +1850,14 @@ pack_single(char *ptr, PyObject *item, const char *fmt)
|
||||||
zd = pylong_as_zd(item);
|
zd = pylong_as_zd(item);
|
||||||
if (zd == -1 && PyErr_Occurred())
|
if (zd == -1 && PyErr_Occurred())
|
||||||
goto err_occurred;
|
goto err_occurred;
|
||||||
|
CHECK_RELEASED_INT_AGAIN(self);
|
||||||
PACK_SINGLE(ptr, zd, Py_ssize_t);
|
PACK_SINGLE(ptr, zd, Py_ssize_t);
|
||||||
break;
|
break;
|
||||||
case 'N':
|
case 'N':
|
||||||
zu = pylong_as_zu(item);
|
zu = pylong_as_zu(item);
|
||||||
if (zu == (size_t)-1 && PyErr_Occurred())
|
if (zu == (size_t)-1 && PyErr_Occurred())
|
||||||
goto err_occurred;
|
goto err_occurred;
|
||||||
|
CHECK_RELEASED_INT_AGAIN(self);
|
||||||
PACK_SINGLE(ptr, zu, size_t);
|
PACK_SINGLE(ptr, zu, size_t);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -1852,6 +1866,7 @@ pack_single(char *ptr, PyObject *item, const char *fmt)
|
||||||
d = PyFloat_AsDouble(item);
|
d = PyFloat_AsDouble(item);
|
||||||
if (d == -1.0 && PyErr_Occurred())
|
if (d == -1.0 && PyErr_Occurred())
|
||||||
goto err_occurred;
|
goto err_occurred;
|
||||||
|
CHECK_RELEASED_INT_AGAIN(self);
|
||||||
if (fmt[0] == 'f') {
|
if (fmt[0] == 'f') {
|
||||||
PACK_SINGLE(ptr, d, float);
|
PACK_SINGLE(ptr, d, float);
|
||||||
}
|
}
|
||||||
|
@ -1865,6 +1880,7 @@ pack_single(char *ptr, PyObject *item, const char *fmt)
|
||||||
ld = PyObject_IsTrue(item);
|
ld = PyObject_IsTrue(item);
|
||||||
if (ld < 0)
|
if (ld < 0)
|
||||||
return -1; /* preserve original error */
|
return -1; /* preserve original error */
|
||||||
|
CHECK_RELEASED_INT_AGAIN(self);
|
||||||
PACK_SINGLE(ptr, ld, _Bool);
|
PACK_SINGLE(ptr, ld, _Bool);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -1882,6 +1898,7 @@ pack_single(char *ptr, PyObject *item, const char *fmt)
|
||||||
p = PyLong_AsVoidPtr(item);
|
p = PyLong_AsVoidPtr(item);
|
||||||
if (p == NULL && PyErr_Occurred())
|
if (p == NULL && PyErr_Occurred())
|
||||||
goto err_occurred;
|
goto err_occurred;
|
||||||
|
CHECK_RELEASED_INT_AGAIN(self);
|
||||||
PACK_SINGLE(ptr, p, void *);
|
PACK_SINGLE(ptr, p, void *);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -2042,7 +2059,7 @@ adjust_fmt(const Py_buffer *view)
|
||||||
|
|
||||||
/* Base case for multi-dimensional unpacking. Assumption: ndim == 1. */
|
/* Base case for multi-dimensional unpacking. Assumption: ndim == 1. */
|
||||||
static PyObject *
|
static PyObject *
|
||||||
tolist_base(const char *ptr, const Py_ssize_t *shape,
|
tolist_base(PyMemoryViewObject *self, const char *ptr, const Py_ssize_t *shape,
|
||||||
const Py_ssize_t *strides, const Py_ssize_t *suboffsets,
|
const Py_ssize_t *strides, const Py_ssize_t *suboffsets,
|
||||||
const char *fmt)
|
const char *fmt)
|
||||||
{
|
{
|
||||||
|
@ -2055,7 +2072,7 @@ tolist_base(const char *ptr, const Py_ssize_t *shape,
|
||||||
|
|
||||||
for (i = 0; i < shape[0]; ptr+=strides[0], i++) {
|
for (i = 0; i < shape[0]; ptr+=strides[0], i++) {
|
||||||
const char *xptr = ADJUST_PTR(ptr, suboffsets, 0);
|
const char *xptr = ADJUST_PTR(ptr, suboffsets, 0);
|
||||||
item = unpack_single(xptr, fmt);
|
item = unpack_single(self, xptr, fmt);
|
||||||
if (item == NULL) {
|
if (item == NULL) {
|
||||||
Py_DECREF(lst);
|
Py_DECREF(lst);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -2069,7 +2086,7 @@ tolist_base(const char *ptr, const Py_ssize_t *shape,
|
||||||
/* Unpack a multi-dimensional array into a nested list.
|
/* Unpack a multi-dimensional array into a nested list.
|
||||||
Assumption: ndim >= 1. */
|
Assumption: ndim >= 1. */
|
||||||
static PyObject *
|
static PyObject *
|
||||||
tolist_rec(const char *ptr, Py_ssize_t ndim, const Py_ssize_t *shape,
|
tolist_rec(PyMemoryViewObject *self, const char *ptr, Py_ssize_t ndim, const Py_ssize_t *shape,
|
||||||
const Py_ssize_t *strides, const Py_ssize_t *suboffsets,
|
const Py_ssize_t *strides, const Py_ssize_t *suboffsets,
|
||||||
const char *fmt)
|
const char *fmt)
|
||||||
{
|
{
|
||||||
|
@ -2081,7 +2098,7 @@ tolist_rec(const char *ptr, Py_ssize_t ndim, const Py_ssize_t *shape,
|
||||||
assert(strides != NULL);
|
assert(strides != NULL);
|
||||||
|
|
||||||
if (ndim == 1)
|
if (ndim == 1)
|
||||||
return tolist_base(ptr, shape, strides, suboffsets, fmt);
|
return tolist_base(self, ptr, shape, strides, suboffsets, fmt);
|
||||||
|
|
||||||
lst = PyList_New(shape[0]);
|
lst = PyList_New(shape[0]);
|
||||||
if (lst == NULL)
|
if (lst == NULL)
|
||||||
|
@ -2089,7 +2106,7 @@ tolist_rec(const char *ptr, Py_ssize_t ndim, const Py_ssize_t *shape,
|
||||||
|
|
||||||
for (i = 0; i < shape[0]; ptr+=strides[0], i++) {
|
for (i = 0; i < shape[0]; ptr+=strides[0], i++) {
|
||||||
const char *xptr = ADJUST_PTR(ptr, suboffsets, 0);
|
const char *xptr = ADJUST_PTR(ptr, suboffsets, 0);
|
||||||
item = tolist_rec(xptr, ndim-1, shape+1,
|
item = tolist_rec(self, xptr, ndim-1, shape+1,
|
||||||
strides+1, suboffsets ? suboffsets+1 : NULL,
|
strides+1, suboffsets ? suboffsets+1 : NULL,
|
||||||
fmt);
|
fmt);
|
||||||
if (item == NULL) {
|
if (item == NULL) {
|
||||||
|
@ -2123,15 +2140,15 @@ memoryview_tolist_impl(PyMemoryViewObject *self)
|
||||||
if (fmt == NULL)
|
if (fmt == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
if (view->ndim == 0) {
|
if (view->ndim == 0) {
|
||||||
return unpack_single(view->buf, fmt);
|
return unpack_single(self, view->buf, fmt);
|
||||||
}
|
}
|
||||||
else if (view->ndim == 1) {
|
else if (view->ndim == 1) {
|
||||||
return tolist_base(view->buf, view->shape,
|
return tolist_base(self, view->buf, view->shape,
|
||||||
view->strides, view->suboffsets,
|
view->strides, view->suboffsets,
|
||||||
fmt);
|
fmt);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return tolist_rec(view->buf, view->ndim, view->shape,
|
return tolist_rec(self, view->buf, view->ndim, view->shape,
|
||||||
view->strides, view->suboffsets,
|
view->strides, view->suboffsets,
|
||||||
fmt);
|
fmt);
|
||||||
}
|
}
|
||||||
|
@ -2339,7 +2356,7 @@ memory_item(PyMemoryViewObject *self, Py_ssize_t index)
|
||||||
char *ptr = ptr_from_index(view, index);
|
char *ptr = ptr_from_index(view, index);
|
||||||
if (ptr == NULL)
|
if (ptr == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
return unpack_single(ptr, fmt);
|
return unpack_single(self, ptr, fmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
PyErr_SetString(PyExc_NotImplementedError,
|
PyErr_SetString(PyExc_NotImplementedError,
|
||||||
|
@ -2370,7 +2387,7 @@ memory_item_multi(PyMemoryViewObject *self, PyObject *tup)
|
||||||
ptr = ptr_from_tuple(view, tup);
|
ptr = ptr_from_tuple(view, tup);
|
||||||
if (ptr == NULL)
|
if (ptr == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
return unpack_single(ptr, fmt);
|
return unpack_single(self, ptr, fmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int
|
static inline int
|
||||||
|
@ -2457,7 +2474,7 @@ memory_subscript(PyMemoryViewObject *self, PyObject *key)
|
||||||
const char *fmt = adjust_fmt(view);
|
const char *fmt = adjust_fmt(view);
|
||||||
if (fmt == NULL)
|
if (fmt == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
return unpack_single(view->buf, fmt);
|
return unpack_single(self, view->buf, fmt);
|
||||||
}
|
}
|
||||||
else if (key == Py_Ellipsis) {
|
else if (key == Py_Ellipsis) {
|
||||||
Py_INCREF(self);
|
Py_INCREF(self);
|
||||||
|
@ -2532,7 +2549,7 @@ memory_ass_sub(PyMemoryViewObject *self, PyObject *key, PyObject *value)
|
||||||
if (key == Py_Ellipsis ||
|
if (key == Py_Ellipsis ||
|
||||||
(PyTuple_Check(key) && PyTuple_GET_SIZE(key)==0)) {
|
(PyTuple_Check(key) && PyTuple_GET_SIZE(key)==0)) {
|
||||||
ptr = (char *)view->buf;
|
ptr = (char *)view->buf;
|
||||||
return pack_single(ptr, value, fmt);
|
return pack_single(self, ptr, value, fmt);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
PyErr_SetString(PyExc_TypeError,
|
PyErr_SetString(PyExc_TypeError,
|
||||||
|
@ -2554,7 +2571,7 @@ memory_ass_sub(PyMemoryViewObject *self, PyObject *key, PyObject *value)
|
||||||
ptr = ptr_from_index(view, index);
|
ptr = ptr_from_index(view, index);
|
||||||
if (ptr == NULL)
|
if (ptr == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
return pack_single(ptr, value, fmt);
|
return pack_single(self, ptr, value, fmt);
|
||||||
}
|
}
|
||||||
/* one-dimensional: fast path */
|
/* one-dimensional: fast path */
|
||||||
if (PySlice_Check(key) && view->ndim == 1) {
|
if (PySlice_Check(key) && view->ndim == 1) {
|
||||||
|
@ -2577,7 +2594,7 @@ memory_ass_sub(PyMemoryViewObject *self, PyObject *key, PyObject *value)
|
||||||
goto end_block;
|
goto end_block;
|
||||||
dest.len = dest.shape[0] * dest.itemsize;
|
dest.len = dest.shape[0] * dest.itemsize;
|
||||||
|
|
||||||
ret = copy_single(&dest, &src);
|
ret = copy_single(self, &dest, &src);
|
||||||
|
|
||||||
end_block:
|
end_block:
|
||||||
PyBuffer_Release(&src);
|
PyBuffer_Release(&src);
|
||||||
|
@ -2593,7 +2610,7 @@ memory_ass_sub(PyMemoryViewObject *self, PyObject *key, PyObject *value)
|
||||||
ptr = ptr_from_tuple(view, key);
|
ptr = ptr_from_tuple(view, key);
|
||||||
if (ptr == NULL)
|
if (ptr == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
return pack_single(ptr, value, fmt);
|
return pack_single(self, ptr, value, fmt);
|
||||||
}
|
}
|
||||||
if (PySlice_Check(key) || is_multislice(key)) {
|
if (PySlice_Check(key) || is_multislice(key)) {
|
||||||
/* Call memory_subscript() to produce a sliced lvalue, then copy
|
/* Call memory_subscript() to produce a sliced lvalue, then copy
|
||||||
|
@ -3194,7 +3211,7 @@ memoryiter_next(memoryiterobject *it)
|
||||||
if (ptr == NULL) {
|
if (ptr == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
return unpack_single(ptr, it->it_fmt);
|
return unpack_single(seq, ptr, it->it_fmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
it->it_seq = NULL;
|
it->it_seq = NULL;
|
||||||
|
|
Loading…
Reference in New Issue