Skip to content

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

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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