You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs/mkdocs/docs/features/binary_formats/messagepack.md states that strings, byte strings, arrays, and objects with more than 4294967295 elements/bytes "can not be converted to a MessagePack value". Nothing in the writer enforces that, and the same page also promises "Any MessagePack output created by to_msgpack can be successfully parsed by from_msgpack".
All four size-prefixed cases in write_msgpack end their if/else if chain at std::uint32_t with no final else:
value type
site
largest branch
string
binary_writer.hpp:554-587
0xDB str 32
array
binary_writer.hpp:589-609
0xDD array 32
binary
binary_writer.hpp:619-686
0xC6/0xC9 bin 32 / ext 32
object
binary_writer.hpp:702-722
0xDF map 32
When N exceeds UINT32_MAX no branch runs, so no header byte is written at all and the writer proceeds straight to the payload. Two failure modes:
array / object — the elements are written through real iterators, so to_msgpack() returns successfully with a headerless, corrupt document.
string / binary — write_characters(data(), N) is then called with the oversized N and reads past the end of the buffer, crashing the process.
CBOR does not have this problem: its chains end in a uint64_t branch (binary_writer.hpp:254-260, 292-298, 355-361, 394-400). UBJSON does not either: it routes sizes through write_number_with_ubjson_prefix, which reaches the 64-bit L marker. MessagePack is the only format whose wire size fields top out at 32 bits, and the only one with no diagnostic.
This is the same defect class as #5314 (to_bson() and INT32_MAX).
Reproduction steps
Serialize a container that reports a size above UINT32_MAX. Using the size-reporting technique from #5314, an array type keeps real iterators so the corrupt output is observable without any out-of-bounds access.
Expected vs. actual results
Expected: a thrown exception, as to_bson() will do after #5314.
Actual, for an array reporting size() == 2^33 with three real elements:
No API/ABI change: no signature, type, or enumerator is affected, and the header stays source- and binary-compatible.
What changes:to_msgpack() gains a throwing path for inputs that today return corrupt output or crash. Since the docs already say these values cannot be converted, the throw enforces a stated limitation rather than introducing a new one. Nobody can be depending on the current behavior in any useful way — the output is unparseable, and half the cases don't even return.
Exception safety is preserved.to_msgpack() documents a strong guarantee; throwing before any bytes are committed keeps that, and the JSON value is never modified.
Docs. The "Size constraints" warning stays accurate; the "Complete mapping" note that promises from_msgpack can parse any to_msgpack output only becomes true once this is fixed.
Testing. As to_bson() silently emits corrupt documents when a length exceeds INT32_MAX #5314 found, reproducing this for real needs multi-gigabyte values, so the practical approach is a file-local type that reports an oversized size() without allocating — the array variant above is the safe one to test with, since the string/binary variants read out of bounds on unpatched code.
The only judgement call is whether an over-large value should throw or be silently truncated to a shorter encoding; truncation would corrupt data just as badly, so throwing seems clearly right and matches #5314.
Description
docs/mkdocs/docs/features/binary_formats/messagepack.mdstates that strings, byte strings, arrays, and objects with more than 4294967295 elements/bytes "can not be converted to a MessagePack value". Nothing in the writer enforces that, and the same page also promises "Any MessagePack output created byto_msgpackcan be successfully parsed byfrom_msgpack".All four size-prefixed cases in
write_msgpackend theirif/else ifchain atstd::uint32_twith no finalelse:binary_writer.hpp:554-5870xDBstr 32binary_writer.hpp:589-6090xDDarray 32binary_writer.hpp:619-6860xC6/0xC9bin 32 / ext 32binary_writer.hpp:702-7220xDFmap 32When
NexceedsUINT32_MAXno branch runs, so no header byte is written at all and the writer proceeds straight to the payload. Two failure modes:to_msgpack()returns successfully with a headerless, corrupt document.write_characters(data(), N)is then called with the oversizedNand reads past the end of the buffer, crashing the process.CBOR does not have this problem: its chains end in a
uint64_tbranch (binary_writer.hpp:254-260,292-298,355-361,394-400). UBJSON does not either: it routes sizes throughwrite_number_with_ubjson_prefix, which reaches the 64-bitLmarker. MessagePack is the only format whose wire size fields top out at 32 bits, and the only one with no diagnostic.This is the same defect class as #5314 (
to_bson()andINT32_MAX).Reproduction steps
Serialize a container that reports a size above
UINT32_MAX. Using the size-reporting technique from #5314, an array type keeps real iterators so the corrupt output is observable without any out-of-bounds access.Expected vs. actual results
Expected: a thrown exception, as
to_bson()will do after #5314.Actual, for an array reporting
size() == 2^33with three real elements:CBOR emits
0x9B+ a 64-bit count, UBJSON emits[#L+ a 64-bit count. MessagePack emits the elements with no header. Reading that back:so in non-strict mode the corruption is silent in both directions.
For
value_t::binaryandvalue_t::stringthe same input instead terminates the process with SIGSEGV insidewrite_characters.Minimal code example
Swapping
huge_arrayfor aBinaryTypethat reports an oversizedsize()reproduces the out-of-bounds read instead.Error messages
None at serialization time — that is the bug.
Compiler and operating system
g++ 13.3.0, Ubuntu 24.04,
-std=c++11Library version
develop@ 8ec98e2Would the fix be breaking?
No API/ABI change: no signature, type, or enumerator is affected, and the header stays source- and binary-compatible.
to_msgpack()gains a throwing path for inputs that today return corrupt output or crash. Since the docs already say these values cannot be converted, the throw enforces a stated limitation rather than introducing a new one. Nobody can be depending on the current behavior in any useful way — the output is unparseable, and half the cases don't even return.to_msgpack()documents a strong guarantee; throwing before any bytes are committed keeps that, and the JSON value is never modified.out_of_range.412for the BSONINT32_MAXcase and ato_bson_length()helper. The MessagePack fix should reuse the same exception id and a parallel helper rather than minting a second one, so the two formats report the same way. If to_bson() silently emits corrupt documents when a length exceeds INT32_MAX #5314 lands first this becomes a small follow-up.from_msgpackcan parse anyto_msgpackoutput only becomes true once this is fixed.size()without allocating — the array variant above is the safe one to test with, since the string/binary variants read out of bounds on unpatched code.The only judgement call is whether an over-large value should throw or be silently truncated to a shorter encoding; truncation would corrupt data just as badly, so throwing seems clearly right and matches #5314.