diff --git a/msgpack/_unpacker.pyx b/msgpack/_unpacker.pyx index e0463617..30ef5e8a 100644 --- a/msgpack/_unpacker.pyx +++ b/msgpack/_unpacker.pyx @@ -168,6 +168,7 @@ def unpackb(object packed, *, object object_hook=None, object list_hook=None, cdef char* buf = NULL cdef Py_ssize_t buf_len cdef const char* cerr = NULL + cdef object extra = None if unicode_errors is not None: cerr = unicode_errors @@ -190,13 +191,17 @@ def unpackb(object packed, *, object object_hook=None, object list_hook=None, use_list, raw, timestamp, strict_map_key, cerr, max_str_len, max_bin_len, max_array_len, max_map_len, max_ext_len) ret = unpack_construct(&ctx, buf, buf_len, &off) + if ret == 1 and off < buf_len: + # buf may point into a temporary contiguous copy owned by view, + # so the extra data must be copied out before releasing view. + extra = PyBytes_FromStringAndSize(buf+off, buf_len-off) finally: PyBuffer_Release(&view); if ret == 1: obj = unpack_data(&ctx) - if off < buf_len: - raise ExtraData(obj, PyBytes_FromStringAndSize(buf+off, buf_len-off)) + if extra is not None: + raise ExtraData(obj, extra) return obj unpack_clear(&ctx) diff --git a/test/test_memoryview.py b/test/test_memoryview.py index 3f6a39d4..fd124dce 100644 --- a/test/test_memoryview.py +++ b/test/test_memoryview.py @@ -2,7 +2,9 @@ from array import array -from msgpack import packb, unpackb +from pytest import raises + +from msgpack import ExtraData, packb, unpackb def make_array(f, data): @@ -109,3 +111,20 @@ def test_unpack_noncontiguous_memoryview(): noncont = memoryview(bytes(padded))[::2] assert not noncont.c_contiguous assert unpackb(noncont) == 2**32 + + +def test_unpack_noncontiguous_memoryview_extra_data(): + # See https://github.com/msgpack/msgpack-python/issues/720 + # ExtraData.extra must be copied out of the temporary contiguous copy + # before that copy is released. + packed = packb(0) + b"extra" + padded = bytearray() + for byte in packed: + padded.append(byte) + padded.append(0) + noncont = memoryview(bytes(padded))[::2] + assert not noncont.c_contiguous + with raises(ExtraData) as excinfo: + unpackb(noncont) + assert excinfo.value.unpacked == 0 + assert excinfo.value.extra == b"extra"