Skip to content
Merged
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
20 changes: 20 additions & 0 deletions cmake/pack_template.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,18 @@
#error msgpack_pack_append_buffer callback is not defined
#endif

/*
* Container and raw lengths travel in a header field that is at most 32
* bits wide. Refuse a size_t that does not fit instead of emitting its low
* 32 bits, which would leave the header disagreeing with the body.
*/
#if SIZE_MAX > UINT32_MAX
#define msgpack_pack_check_container_size(n) \
do { if((n) > UINT32_MAX) { return -1; } } while(0)
#else
#define msgpack_pack_check_container_size(n) do { } while(0)
#endif

#if defined(_MSC_VER)
# pragma warning(push)
# pragma warning(disable : 4204) /* nonstandard extension used: non-constant aggregate initializer */
Expand Down Expand Up @@ -719,6 +731,7 @@ msgpack_pack_inline_func(_false)(msgpack_pack_user x)

msgpack_pack_inline_func(_array)(msgpack_pack_user x, size_t n)
{
msgpack_pack_check_container_size(n);
if(n < 16) {
unsigned char d = 0x90 | (uint8_t)n;
msgpack_pack_append_buffer(x, &d, 1);
Expand All @@ -740,6 +753,7 @@ msgpack_pack_inline_func(_array)(msgpack_pack_user x, size_t n)

msgpack_pack_inline_func(_map)(msgpack_pack_user x, size_t n)
{
msgpack_pack_check_container_size(n);
if(n < 16) {
unsigned char d = 0x80 | (uint8_t)n;
msgpack_pack_append_buffer(x, &TAKE8_8(d), 1);
Expand All @@ -761,6 +775,7 @@ msgpack_pack_inline_func(_map)(msgpack_pack_user x, size_t n)

msgpack_pack_inline_func(_str)(msgpack_pack_user x, size_t l)
{
msgpack_pack_check_container_size(l);
if(l < 32) {
unsigned char d = 0xa0 | (uint8_t)l;
msgpack_pack_append_buffer(x, &TAKE8_8(d), 1);
Expand Down Expand Up @@ -790,6 +805,7 @@ msgpack_pack_inline_func(_str_body)(msgpack_pack_user x, const void* b, size_t l

msgpack_pack_inline_func(_v4raw)(msgpack_pack_user x, size_t l)
{
msgpack_pack_check_container_size(l);
if(l < 32) {
unsigned char d = 0xa0 | (uint8_t)l;
msgpack_pack_append_buffer(x, &TAKE8_8(d), 1);
Expand All @@ -815,6 +831,7 @@ msgpack_pack_inline_func(_v4raw_body)(msgpack_pack_user x, const void* b, size_t

msgpack_pack_inline_func(_bin)(msgpack_pack_user x, size_t l)
{
msgpack_pack_check_container_size(l);
if(l < 256) {
unsigned char buf[2];
buf[0] = 0xc4; buf[1] = (uint8_t)l;
Expand All @@ -841,6 +858,7 @@ msgpack_pack_inline_func(_bin_body)(msgpack_pack_user x, const void* b, size_t l

msgpack_pack_inline_func(_ext)(msgpack_pack_user x, size_t l, int8_t type)
{
msgpack_pack_check_container_size(l);
switch(l) {
case 1: {
unsigned char buf[2];
Expand Down Expand Up @@ -938,6 +956,8 @@ msgpack_pack_inline_func(_timestamp)(msgpack_pack_user x, const msgpack_timestam
#undef TAKE8_32
#undef TAKE8_64

#undef msgpack_pack_check_container_size

#undef msgpack_pack_real_uint8
#undef msgpack_pack_real_uint16
#undef msgpack_pack_real_uint32
Expand Down
40 changes: 40 additions & 0 deletions test/msgpack_c.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -681,6 +681,46 @@ TEST(MSGPACKC, simple_buffer_ext_maxlen)
}
#endif // SIZE_MAX > UINT32_MAX

// The length field of a str/bin/ext/array/map header is at most 32 bits
// wide. A size_t that does not fit is rejected rather than narrowed into a
// header that disagrees with the body the caller appends next.
#if SIZE_MAX > UINT32_MAX
TEST(MSGPACKC, pack_length_over_uint32)
{
// 2^32 + 1: the low 32 bits say "1 byte"
const size_t over = static_cast<size_t>(UINT32_MAX) + 2;

msgpack_sbuffer sbuf;
msgpack_sbuffer_init(&sbuf);
msgpack_packer pk;
msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);

EXPECT_EQ(-1, msgpack_pack_str(&pk, over));
EXPECT_EQ(-1, msgpack_pack_v4raw(&pk, over));
EXPECT_EQ(-1, msgpack_pack_bin(&pk, over));
EXPECT_EQ(-1, msgpack_pack_ext(&pk, over, 1));
EXPECT_EQ(-1, msgpack_pack_array(&pk, over));
EXPECT_EQ(-1, msgpack_pack_map(&pk, over));
// a rejected header leaves nothing in the buffer
EXPECT_EQ(0u, sbuf.size);

// the largest encodable length still goes out as str32 / ext32
EXPECT_EQ(0, msgpack_pack_str(&pk, static_cast<size_t>(UINT32_MAX)));
EXPECT_EQ(5u, sbuf.size);
EXPECT_EQ(static_cast<char>(0xdbu), sbuf.data[0]);
EXPECT_EQ(static_cast<char>(0xffu), sbuf.data[1]);
EXPECT_EQ(static_cast<char>(0xffu), sbuf.data[4]);
EXPECT_EQ(0, msgpack_pack_ext(&pk, static_cast<size_t>(UINT32_MAX), 1));
EXPECT_EQ(11u, sbuf.size);
EXPECT_EQ(static_cast<char>(0xc9u), sbuf.data[5]);
EXPECT_EQ(static_cast<char>(0xffu), sbuf.data[6]);
EXPECT_EQ(static_cast<char>(0xffu), sbuf.data[9]);
EXPECT_EQ(static_cast<char>(0x01u), sbuf.data[10]);

msgpack_sbuffer_destroy(&sbuf);
}
#endif // SIZE_MAX > UINT32_MAX

TEST(MSGPACKC, simple_buffer_timestamp_32)
{
msgpack_timestamp ts = {
Expand Down
Loading