Skip to content

Commit ea4bd9d

Browse files
fix: use-after-free in unpackb() ExtraData path for non-contiguous input
For non-contiguous input, get_data_from_buffer() makes a temporary contiguous copy and buf points into it. unpackb() released the buffer view (freeing that copy) in the finally block, and only afterwards read buf+off to build the ExtraData payload, so ExtraData.extra was filled from freed memory. Copy the extra data out before releasing the view. Fixes #720
1 parent 3f5d0d7 commit ea4bd9d

2 files changed

Lines changed: 27 additions & 3 deletions

File tree

msgpack/_unpacker.pyx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ def unpackb(object packed, *, object object_hook=None, object list_hook=None,
168168
cdef char* buf = NULL
169169
cdef Py_ssize_t buf_len
170170
cdef const char* cerr = NULL
171+
cdef object extra = None
171172

172173
if unicode_errors is not None:
173174
cerr = unicode_errors
@@ -190,13 +191,17 @@ def unpackb(object packed, *, object object_hook=None, object list_hook=None,
190191
use_list, raw, timestamp, strict_map_key, cerr,
191192
max_str_len, max_bin_len, max_array_len, max_map_len, max_ext_len)
192193
ret = unpack_construct(&ctx, buf, buf_len, &off)
194+
if ret == 1 and off < buf_len:
195+
# buf may point into a temporary contiguous copy owned by view,
196+
# so the extra data must be copied out before releasing view.
197+
extra = PyBytes_FromStringAndSize(buf+off, buf_len-off)
193198
finally:
194199
PyBuffer_Release(&view);
195200

196201
if ret == 1:
197202
obj = unpack_data(&ctx)
198-
if off < buf_len:
199-
raise ExtraData(obj, PyBytes_FromStringAndSize(buf+off, buf_len-off))
203+
if extra is not None:
204+
raise ExtraData(obj, extra)
200205
return obj
201206

202207
unpack_clear(&ctx)

test/test_memoryview.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
from array import array
44

5-
from msgpack import packb, unpackb
5+
from pytest import raises
6+
7+
from msgpack import ExtraData, packb, unpackb
68

79

810
def make_array(f, data):
@@ -109,3 +111,20 @@ def test_unpack_noncontiguous_memoryview():
109111
noncont = memoryview(bytes(padded))[::2]
110112
assert not noncont.c_contiguous
111113
assert unpackb(noncont) == 2**32
114+
115+
116+
def test_unpack_noncontiguous_memoryview_extra_data():
117+
# See https://github.com/msgpack/msgpack-python/issues/720
118+
# ExtraData.extra must be copied out of the temporary contiguous copy
119+
# before that copy is released.
120+
packed = packb(0) + b"extra"
121+
padded = bytearray()
122+
for byte in packed:
123+
padded.append(byte)
124+
padded.append(0)
125+
noncont = memoryview(bytes(padded))[::2]
126+
assert not noncont.c_contiguous
127+
with raises(ExtraData) as excinfo:
128+
unpackb(noncont)
129+
assert excinfo.value.unpacked == 0
130+
assert excinfo.value.extra == b"extra"

0 commit comments

Comments
 (0)