Skip to content

Commit 091ae20

Browse files
fix: use-after-free in unpackb() ExtraData path for non-contiguous input (#722)
Fixes #720. # Problem For non-contiguous input, `get_data_from_buffer()` releases the original view and makes a temporary contiguous copy, so `buf` points into memory owned by `view`.
1 parent 3f5d0d7 commit 091ae20

2 files changed

Lines changed: 28 additions & 7 deletions

File tree

msgpack/_unpacker.pyx

Lines changed: 8 additions & 6 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,15 +191,16 @@ 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:
195+
obj = unpack_data(&ctx)
196+
if off < buf_len:
197+
# buf may point into a temporary contiguous copy owned by view,
198+
# so the extra data must be copied out before releasing view.
199+
raise ExtraData(obj, PyBytes_FromStringAndSize(buf+off, buf_len-off))
200+
return obj
193201
finally:
194202
PyBuffer_Release(&view);
195203

196-
if ret == 1:
197-
obj = unpack_data(&ctx)
198-
if off < buf_len:
199-
raise ExtraData(obj, PyBytes_FromStringAndSize(buf+off, buf_len-off))
200-
return obj
201-
202204
unpack_clear(&ctx)
203205
if ret == 0:
204206
raise ValueError("Unpack failed: incomplete input")

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)