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.c — processBulkItem:
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):
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:
- Treat an incomplete bulk as complete and advance the reader incorrectly (protocol desynchronization).
- 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.
Summary
On Windows x64 (LLP64), where
sizeof(unsigned long) == 4andsizeof(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 anunsigned long bytelen, while the protocol length is along long. Addinglen + 2intobytelenwraps. The completeness check then uses the truncated value, butcreateStringis called with the original largelen.This is not CVE-2021-32765 (multi-bulk
callocelement-count overflow).Affected code
read.c—processBulkItem:Platform detail
unsigned longlong longsize_tOn LP64 Linux the same
len = 2^32addition does not wrap inunsigned long, so this specific trigger is LLP64-oriented. On pure 32-bit,len > SIZE_MAXis rejected by the existing range check.Proof of concept (local, no network)
Environment:
x86_64-w64-mingw32,sizeof(long)==4.Incomplete bulk (claims
2^32bytes, only 2 payload bytes present):Arithmetic:
With a custom
createStringthat only recordslen(avoids allocating 4GiB):Default path with stock
createStringObjecttypically hits OOM onhi_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:
createStringwith a hugelen→ OOM DoS, or OOB read if allocation succeeds / custom callbacks trustlen.Suggested fix
Use a type that cannot truncate relative to
size_t/long long, and check overflow before the completeness test:Alternatively change
bytelentosize_tand reject whenlen + 2would wrap. Consider a configurable max bulk length similar tomaxelements.Duplicate check (2026-07-22)
r->pos == r->lenin parse path; differentunsigned long bytelentruncation on bulk stringsCWE
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.