Summary
The core bounds-checking macros cw_unpack_assert_space_sub (cwpack_internals.h:287) and cw_pack_reserve_space (cwpack_internals.h:138) use pointer arithmetic that overflows on 32-bit platforms, completely bypassing buffer boundary validation.
Vulnerability Details
CWE-190: Integer Overflow or Wraparound
In cwpack_internals.h, the unpack bounds check macro:
#define cw_unpack_assert_space_sub(more,abortValue)
{
p = unpack_context->current;
uint8_t* nyp = p + more; // <-- wraps on 32-bit
if (nyp > unpack_context->end) // <-- bypassed
...
}
When unpacking str32 (0xdb), bin32 (0xc6), or ext32 (0xc9) types, the more parameter comes from a uint32_t length field read directly from untrusted msgpack data. On 32-bit platforms (ARM Cortex-M, ESP32, etc.), if more is large enough, p + more wraps around the 32-bit address space, producing a pointer less than end, so the check passes.
After the check passes, cw_unpack_assert_blob sets .start = p and .length = <attacker_value>, allowing the caller to read up to 4GB past the buffer boundary.
Additional Issues
1. Integer overflow in cw_skip_items (cwpack.c:714)
case 0xdf: // map 32
cw_unpack_assert_space(4);
cw_load32(p);
item_count += 2*tmpu32; // unsigned multiplication wraps
break;
2*tmpu32 wraps when tmpu32 >= 0x80000000, causing item_count to increase by far less than the actual map size. The skip function then terminates early, leaving the unpack cursor misaligned. Subsequent calls to cw_unpack_next interpret map values as message structure, causing type confusion.
2. Integer overflow in pack path (cwpack.c:276, 314, 372)
// In cw_pack_str, line 276:
cw_pack_reserve_space(l+5) // l is uint32_t; if l = 0xFFFFFFFB, l+5 = 0
*p++ = (uint8_t)0xdb;
cw_store32(l);
memcpy(p+4,v,l); // copies ~4GB with 0 bytes reserved
Same pattern in cw_pack_bin and cw_pack_ext.
Impact
- Primary target: 32-bit embedded systems (ARM Cortex-M, ESP32, RISC-V) — exactly the platforms where a lightweight 1200-line msgpack library would be deployed
- Severity: High — complete bounds check bypass enables arbitrary out-of-bounds reads (information disclosure) and potentially writes
- Attack vector: Crafted msgpack input with large str32/bin32/ext32 length fields
Suggested Fix
Replace pointer arithmetic checks with length-based checks:
// Instead of:
uint8_t* nyp = p + more;
if (nyp > unpack_context->end)
// Use:
if (more > (size_t)(unpack_context->end - p))
This avoids pointer overflow entirely. The same pattern should be applied to cw_pack_reserve_space.
Summary
The core bounds-checking macros
cw_unpack_assert_space_sub(cwpack_internals.h:287) andcw_pack_reserve_space(cwpack_internals.h:138) use pointer arithmetic that overflows on 32-bit platforms, completely bypassing buffer boundary validation.Vulnerability Details
CWE-190: Integer Overflow or Wraparound
In
cwpack_internals.h, the unpack bounds check macro:When unpacking
str32(0xdb),bin32(0xc6), orext32(0xc9) types, themoreparameter comes from auint32_tlength field read directly from untrusted msgpack data. On 32-bit platforms (ARM Cortex-M, ESP32, etc.), ifmoreis large enough,p + morewraps around the 32-bit address space, producing a pointer less thanend, so the check passes.After the check passes,
cw_unpack_assert_blobsets.start = pand.length = <attacker_value>, allowing the caller to read up to 4GB past the buffer boundary.Additional Issues
1. Integer overflow in
cw_skip_items(cwpack.c:714)2*tmpu32wraps whentmpu32 >= 0x80000000, causingitem_countto increase by far less than the actual map size. The skip function then terminates early, leaving the unpack cursor misaligned. Subsequent calls tocw_unpack_nextinterpret map values as message structure, causing type confusion.2. Integer overflow in pack path (cwpack.c:276, 314, 372)
Same pattern in
cw_pack_binandcw_pack_ext.Impact
Suggested Fix
Replace pointer arithmetic checks with length-based checks:
This avoids pointer overflow entirely. The same pattern should be applied to
cw_pack_reserve_space.