Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 17 additions & 5 deletions msgpack/_packer.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ cdef class Packer:
cdef const char* rawval
cdef Py_ssize_t L
cdef Py_buffer view
cdef object odata
cdef bint strict = self.strict_types

if o is None:
Expand Down Expand Up @@ -213,8 +214,12 @@ cdef class Packer:
self._pack(v, nest_limit)
elif type(o) is ExtType if strict else isinstance(o, ExtType):
# This should be before Tuple because ExtType is namedtuple.
rawval = o.data
L = len(o.data)
# Size the body from the bytes object itself. len() can be
# overridden on a bytes subclass so it disagrees with rawval,
# which would make msgpack_pack_raw_body read past the buffer.
odata = o.data
rawval = odata
L = Py_SIZE(odata)
if L > ITEM_LIMIT:
raise ValueError("EXT data is too large")
msgpack_pack_ext(&self.pk, <long>o.code, L)
Expand Down Expand Up @@ -287,11 +292,18 @@ cdef class Packer:

@cython.critical_section
def pack_ext_type(self, typecode, data):
cdef const char *rawval
cdef Py_ssize_t L
self._check_exports()
if len(data) > ITEM_LIMIT:
# Take the body length from the bytes object, not len(): a bytes
# subclass can override __len__ so it no longer matches rawval and
# msgpack_pack_raw_body reads past the buffer.
rawval = data
L = Py_SIZE(data)
if L > ITEM_LIMIT:
raise ValueError("ext data too large")
msgpack_pack_ext(&self.pk, typecode, len(data))
msgpack_pack_raw_body(&self.pk, data, len(data))
msgpack_pack_ext(&self.pk, typecode, L)
msgpack_pack_raw_body(&self.pk, rawval, L)
if self.autoreset:
buf = PyBytes_FromStringAndSize(self.pk.buf, self.pk.length)
self.pk.length = 0
Expand Down
23 changes: 23 additions & 0 deletions test/test_extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,29 @@ def test_pack_ext_type_autoreset():
assert packer.bytes() == b""


@pytest.mark.skipif(
msgpack.Packer.__module__ == "msgpack.fallback",
reason="out-of-bounds read is specific to the C extension",
)
def test_pack_ext_type_len_override():
# A bytes subclass whose __len__ over-reports its size must not make the
# packer copy more than the real buffer.
class BadLen(bytes):
def __len__(self):
return 4096

data = BadLen(b"AB") # real length 2
assert bytes.__len__(data) == 2

packed = msgpack.packb(ExtType(0x42, data))
assert packed == b"\xd5\x42AB" # fixext 2, sized by the real buffer
assert msgpack.unpackb(packed) == ExtType(0x42, b"AB")

packer = msgpack.Packer(autoreset=False)
packer.pack_ext_type(0x42, data)
assert packer.bytes() == b"\xd5\x42AB"


def test_unpack_ext_type():
def check(b, expected):
assert msgpack.unpackb(b) == expected
Expand Down
Loading