From 9086469e18faefb0fd8323840ec2b0b14eb2a1f7 Mon Sep 17 00:00:00 2001 From: Thomas Kowalski Date: Tue, 26 May 2026 16:23:01 +0200 Subject: [PATCH 1/3] test: add test --- test/test_memoryview.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/test_memoryview.py b/test/test_memoryview.py index 0a2a6f53..3f6a39d4 100644 --- a/test/test_memoryview.py +++ b/test/test_memoryview.py @@ -97,3 +97,15 @@ def test_multidim_memoryview(): data = view.cast(view.format, (3, 2)) packed = packb(data) assert packed == b"\xc4\x06\x00\x00\x00\x00\x00\x00" + + +def test_unpack_noncontiguous_memoryview(): + # Use a multi-byte value so the padded stride-2 view is non-contiguous. + packed = packb(2**32) + padded = bytearray() + for byte in packed: + padded.append(byte) + padded.append(0) + noncont = memoryview(bytes(padded))[::2] + assert not noncont.c_contiguous + assert unpackb(noncont) == 2**32 From eae29a954e11261313fc4ba1ef05d91faa3bb4a0 Mon Sep 17 00:00:00 2001 From: Thomas Kowalski Date: Tue, 26 May 2026 11:58:19 +0200 Subject: [PATCH 2/3] fix: use-after-free in get_data_from_buffer --- msgpack/_unpacker.pyx | 4 +--- msgpack/fallback.py | 3 ++- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/msgpack/_unpacker.pyx b/msgpack/_unpacker.pyx index e25986ee..da6acbbd 100644 --- a/msgpack/_unpacker.pyx +++ b/msgpack/_unpacker.pyx @@ -130,9 +130,7 @@ cdef inline int get_data_from_buffer(object obj, # create a contiguous copy and get buffer contiguous = PyMemoryView_GetContiguous(obj, PyBUF_READ, b'C') PyObject_GetBuffer(contiguous, view, PyBUF_SIMPLE) - # view must hold the only reference to contiguous, - # so memory is freed when view is released - Py_DECREF(contiguous) + buffer_len[0] = view.len buf[0] = view.buf return 1 diff --git a/msgpack/fallback.py b/msgpack/fallback.py index 1f2daf7b..159dff2e 100644 --- a/msgpack/fallback.py +++ b/msgpack/fallback.py @@ -328,7 +328,8 @@ def feed(self, next_bytes): self._buf_checkpoint = 0 # Use extend here: INPLACE_ADD += doesn't reliably typecast memoryview in jython - self._buffer.extend(view) + # tobytes ensures compatibility with non-contiguous memoryviews + self._buffer.extend(view.tobytes()) view.release() def _consume(self): From 7ee15a324b50feb2c86fc31152b099045f5fd02e Mon Sep 17 00:00:00 2001 From: Thomas Kowalski Date: Wed, 27 May 2026 14:39:44 +0200 Subject: [PATCH 3/3] fix: address PR review comments - Check PyObject_GetBuffer return value and raise on failure - Avoid unnecessary tobytes() copy for contiguous memoryviews in fallback feed() --- msgpack/_unpacker.pyx | 3 ++- msgpack/fallback.py | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/msgpack/_unpacker.pyx b/msgpack/_unpacker.pyx index da6acbbd..fc283f3c 100644 --- a/msgpack/_unpacker.pyx +++ b/msgpack/_unpacker.pyx @@ -129,7 +129,8 @@ cdef inline int get_data_from_buffer(object obj, PyBuffer_Release(view) # create a contiguous copy and get buffer contiguous = PyMemoryView_GetContiguous(obj, PyBUF_READ, b'C') - PyObject_GetBuffer(contiguous, view, PyBUF_SIMPLE) + if PyObject_GetBuffer(contiguous, view, PyBUF_SIMPLE) == -1: + raise buffer_len[0] = view.len buf[0] = view.buf diff --git a/msgpack/fallback.py b/msgpack/fallback.py index 159dff2e..e4cb9873 100644 --- a/msgpack/fallback.py +++ b/msgpack/fallback.py @@ -328,8 +328,7 @@ def feed(self, next_bytes): self._buf_checkpoint = 0 # Use extend here: INPLACE_ADD += doesn't reliably typecast memoryview in jython - # tobytes ensures compatibility with non-contiguous memoryviews - self._buffer.extend(view.tobytes()) + self._buffer.extend(view if view.contiguous else view.tobytes()) view.release() def _consume(self):