Skip to content

[SECURITY] Integer truncation in processBulkItem on LLP64 (Windows x64) treats incomplete bulk as complete #1333

Description

@LL-V

Summary

On Windows x64 (LLP64), where sizeof(unsigned long) == 4 and sizeof(long long) == sizeof(size_t) == 8, hiredis can treat an incomplete RESP bulk string as complete when the declared bulk length is large (e.g. 2^32).

In processBulkItem (read.c), the total byte span of the bulk item is accumulated in an unsigned long bytelen, while the protocol length is a long long. Adding len + 2 into bytelen wraps. The completeness check then uses the truncated value, but createString is called with the original large len.

This is not CVE-2021-32765 (multi-bulk calloc element-count overflow).

Affected code

read.cprocessBulkItem:

unsigned long bytelen;
/* ... */
bytelen = s-(r->buf+r->pos)+2; /* include \r\n */
/* string2ll -> long long len */
if (len < -1 || (LLONG_MAX > SIZE_MAX && len > (long long)SIZE_MAX)) {
    /* On 64-bit size_t, LLONG_MAX > SIZE_MAX is false — large len accepted */
}
/* ... */
bytelen += len+2; /* LLP64: truncates when len is near 2^32 */
if (r->pos+bytelen <= r->len) {
    obj = r->fn->createString(cur,s+2,len); /* uses full len */
}

Platform detail

Type Windows x64 (LLP64) Linux x86_64 (LP64)
unsigned long 4 8
long long 8 8
size_t 8 8

On LP64 Linux the same len = 2^32 addition does not wrap in unsigned long, so this specific trigger is LLP64-oriented. On pure 32-bit, len > SIZE_MAX is rejected by the existing range check.

Proof of concept (local, no network)

Environment: x86_64-w64-mingw32, sizeof(long)==4.

Incomplete bulk (claims 2^32 bytes, only 2 payload bytes present):

$4294967296\r\nAB

Arithmetic:

bytelen = 13;                 /* length of "$4294967296\r\n" */
bytelen += 0x100000000 + 2;   /* wraps to 15 on LLP64 */
/* r->pos + 15 <= r->len (15) succeeds */

With a custom createString that only records len (avoids allocating 4GiB):

feed ret=0
getReply ret=0
createString called len=4294967296
RESULT: incomplete bulk accepted as complete

Default path with stock createStringObject typically hits OOM on hi_malloc(len+1) after incorrectly accepting the bulk as complete (DoS / protocol confusion). If allocation succeeds (custom allocator / overcommit), memcpy(buf, str, len) is an out-of-bounds read from the short reader buffer.

Impact

Any hiredis client on LLP64 that parses RESP from an untrusted Redis server (or MITM) can be forced to:

  1. Treat an incomplete bulk as complete and advance the reader incorrectly (protocol desynchronization).
  2. Invoke createString with a huge len → OOM DoS, or OOB read if allocation succeeds / custom callbacks trust len.

Suggested fix

Use a type that cannot truncate relative to size_t / long long, and check overflow before the completeness test:

size_t hdrlen = (size_t)(s - (r->buf + r->pos) + 2);
if (len >= 0) {
    if ((unsigned long long)len > (unsigned long long)SIZE_MAX - hdrlen - 2)
        /* protocol error: bulk string length out of range */;
    size_t total = hdrlen + (size_t)len + 2;
    if (r->pos > SIZE_MAX - total || r->pos + total > r->len)
        return REDIS_ERR; /* need more data */
    /* createString(..., len); r->pos += total; */
}

Alternatively change bytelen to size_t and reject when len + 2 would wrap. Consider a configurable max bulk length similar to maxelements.

Duplicate check (2026-07-22)

CWE

  • CWE-190 Integer Overflow / Truncation
  • CWE-125 Out-of-bounds Read (when allocation succeeds or custom callbacks)

Credit

LL-V — local source audit + gcc PoC on mingw-w64.
GitHub: https://github.com/LL-V

Please credit if a CVE is requested. Happy to retest a patch.

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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions