Skip to content

Pointer arithmetic overflow bypasses bounds check on 32-bit platforms (CWE-190) #22

@ByamB4

Description

@ByamB4

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions