LLP64 bulk-length truncation in hiredis processBulkItem → heap out-of-bounds read / DoS
Integer truncation of the bulk-string length on LLP64 targets (Windows x64) in the hiredis RESP reader leads to a heap out-of-bounds read / denial of service (incomplete fix of the 2018 bulk-length-truncation guard, commit 1091975; related to CVE-2021-32765)
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H
Background
hiredis is the official minimal C client for Redis, embedded in a very large number of C/C++
applications. Its reply reader (read.c) is a stand-alone RESP (REdis Serialization Protocol)
parser: bytes arriving from a Redis server are handed to redisReaderFeed() and turned into
redisReply objects by redisReaderGetReply(). The higher-level redisCommand() /
redisGetReply() drive exactly this parser over the socket, so every byte the server sends is
parsed by this code.
RESP encodes a bulk string as a length line followed by the payload:
$<len>\r\n<len bytes of payload>\r\n
<len> is attacker-controlled decimal digits: the client trusts the server to state the payload
size. Commit 1091975 (2018-05-20, "Fix bulk and multi-bulk length truncation") added a range
check to processBulkItem precisely to stop a malicious <len> from truncating and under-sizing
the copy — its commit message states "processBulkItem had the same truncation issue … createString
takes size_t, so bound to SIZE_MAX." The later CVE-2021-32765 fix (commit 76a7b1000, 2021, in
hiredis 1.0.1) hardened the sibling multi-bulk count path but did not touch this bulk-string guard.
This advisory shows that the 2018 bulk-string guard is incomplete on LLP64 targets: it silently
fails to bound <len> and a subsequent 32-bit truncation re-opens the very truncation bug it was
meant to fix.
Description
processBulkItem() stores the running item size in a variable of type unsigned long. On the
LP64 platforms the fix was validated against, unsigned long is 64-bit and matches size_t, so
the guard and the size arithmetic behave as intended. On Windows (both x64 LLP64 and 32-bit
ILP32), unsigned long is 32-bit while size_t is 64-bit (x64) — two independent
consequences follow:
-
The range check added by that 2018 fix (commit
1091975),len > (long long)SIZE_MAX, is written behindLLONG_MAX > SIZE_MAX, which is false on every 64-bit target, so that clause is dead code andlenis only floored at-1. An attacker may declarelenup toLLONG_MAX. -
The "is the whole item present in the buffer yet?" size,
bytelen, is computed in 64-bit but stored back into the 32-bitunsigned long, truncating modulo 2³². By choosinglen ≈ 2³²the attacker makes the truncatedbytelentiny, so the completeness check passes with only the length line in the buffer — while the untruncated 64-bitlenis then passed tocreateStringObject(), which allocates andmemcpys that full length from a source buffer that holds two bytes. The copy runs off the end of the reader buffer → heap out-of-bounds read → the process crashes.
Technical details
Affected code
read.c, processBulkItem() (HEAD 1.5.0-dev, lines as shown; the same code is at
read.c:402-444 in release 1.3.0):
static int processBulkItem(redisReader *r) {
...
long long len; /* L459 — 64-bit */
unsigned long bytelen; /* L460 — 32-bit on LLP64/ILP32 (Windows) */
...
if (string2ll(p, bytelen - 2, &len) == REDIS_ERR) { ...bad length... }
if (len < -1 || (LLONG_MAX > SIZE_MAX && len > (long long)SIZE_MAX)) { /* L475 */
...out of range... /* second clause is DEAD on any 64-bit box */
}
...
} else {
bytelen += len+2; /* L490 — 64-bit math, TRUNCATED into 32-bit bytelen */
if (r->pos+bytelen <= r->len) { /* L491 — completeness check uses the truncated value */
...
obj = r->fn->createString(cur,s+2,len); /* L501 — copies the FULL 64-bit len */
...
The oversized copy itself, createStringObject() in hiredis.c (≈ lines 151-154):
buf = hi_malloc(len+1); /* len ≈ 2^32 → ~4 GB allocation */ ... memcpy(buf,str,len); /* reads ~4 GB from a 2-byte source → OOB read → crash */
The RESP3 verbatim string type (=) reaches the same processBulkItem path and the
createStringObject VERB branch (hiredis.c ≈ 142-147), so it is affected identically.
Root cause
The 2018 truncation fix (commit 1091975) implicitly assumed sizeof(unsigned long) == sizeof(size_t). That holds on
LP64 but not on Windows, where unsigned long is 32-bit. Two defects compound: a range check
that is dead on 64-bit (LLONG_MAX > SIZE_MAX), and a size accumulator (bytelen) narrower than
the value it must hold.
Attacker control
The full RESP byte stream, including the bulk-string length line $<len>\r\n (and the RESP3
verbatim =<len>\r\n). The attacker chooses len so that (header + len + 2) mod 2³² is small.
The minimum viable len is ≈ 2³² (about 4 GB); the payload bytes need not be present.
Reachability
The parser is fed by redisReaderGetReply(), which redisCommand() / redisGetReply() invoke on
every reply. Any hiredis consumer that connects to a Redis endpoint and issues commands returning
bulk strings (GET, HGET, HGETALL, LRANGE, SCAN values, …) reaches processBulkItem on
attacker-controlled bytes if:
- the Redis server is malicious or has been compromised, or
- an attacker is on-path on the (by default cleartext) client↔server connection (MITM).
Either condition lets the server-side bytes be fully chosen by the attacker.
Proof of concept
The crafted reply (15 bytes)
24 34 32 39 34 39 36 37 32 39 36 0d 0a 58 58 $4294967296\r\nXX
$ + the ASCII digits 4294967296 (= 2³²) + \r\n + two filler bytes XX.
Walk-through on Windows x64 (LLP64):
len = 4294967296. The L475 guard passes (dead second clause;lenonly floored at-1).bytelen = 12(10 digit bytes +\r\n), thenbytelen += len + 2 = 4294967310; stored into the 32-bitunsigned longthis becomes 14.r->pos(1) + 14 = 15 ≤ r->len(15)→ the completeness check passes with the whole 15-byte buffer.createStringObject(cur, s+2, 4294967296)→hi_malloc(~4 GB)succeeds, thenmemcpy(dst, s+2, ~4 GB)reads off the end of the 15-byte reader buffer → access violation.
On LP64, step 2 keeps bytelen = 4294967310 (64-bit, no truncation), so
r->pos + bytelen (4294967311) ≤ r->len (15) is false: the reader simply waits for more data.
No crash. This divergence is the confirmation.
Harness (essential lines)
A byte-for-byte stand-in for how redisGetReply() consumes the socket — feed the bytes to the
reader in small chunks and pull replies:
redisReader *r = redisReaderCreate();
/* feed the file in 64-byte chunks, exactly like socket reads */
redisReaderFeed(r, chunk, n);
void *reply;
while (redisReaderGetReply(r, &reply) == REDIS_OK && reply) { freeReplyObject(reply); }
Results
Windows x64, built with MSVC 14.38 (cl /Od /Zi, LLP64), hiredis HEAD 574c1586:
> hr_harness_win.exe good.resp (control: $16\r\n4111111111111111\r\n)
entrada=23 bytes
replies=1 rc=0 → clean
> hr_harness_win.exe poc.resp ($4294967296\r\nXX, 15 bytes)
entrada=15 bytes
→ process exit code -1073741819 = 0xC0000005
(ACCESS VIOLATION; the "replies=" line is never reached)
Linux x64, built with GCC (LP64), same sources, same 15-byte input — cross-platform control:
$ ./hr_harness poc.resp replies=0 rc=0 → no crash; reader waits for more data $ valgrind -q ./hr_harness poc.resp (no errors) → clean; the defect is absent on LP64
The control (a well-formed bulk string) parses cleanly on both platforms; only the crafted length crashes, and only on the LLP64 build.
Risk rating (CVSS 3.1)
6.5 (Medium) — AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H. The base with UI:N is 7.5 (High).
Network vector: the malicious bytes originate from the Redis server (or an on-path attacker). Attack complexity is Low — a single short reply. Availability is High: the process crashes. Confidentiality is scored None: although this is technically an out-of-bounds read, the copy faults before the data is usable by the caller, so it is a crash, not a practical information leak.
Three honest qualifications place the practical severity below a bare network High:
- Platform-gated. Only LLP64/ILP32 (Windows) builds are affected. LP64 builds are not.
- Feature-gated. The consumer must actually connect to the attacker-influenced Redis endpoint
(in the affected product, an opt-in remote-database scan); a build that never talks to an
untrusted Redis is not exposed. This is why we score
UI:R. - Allocation-gated. The crash requires the ~4 GB destination allocation to succeed (then the read faults). If it fails, the result is a clean out-of-memory error, not a crash. On hosts with ample memory/commit — the usual case for the workstations that run such scans — the allocation succeeds and the crash is reliable; we reproduced it directly.
Business impact
An application that uses hiredis to read from a Redis endpoint it does not fully control — a compromised server, a server reachable across a trust boundary, or any deployment where the client↔server link can be intercepted — can be crashed by a single crafted bulk reply when built for Windows. In a per-connection or per-request worker model, one crafted reply aborts the worker, denying service. Because the trigger is in the reply reader, ordinary application-level error handling around the command call does not prevent the process from terminating.
Systems affected
- hiredis HEAD
1.5.0-dev(commit574c1586) and release1.3.0, on Windows x64 (LLP64) and Windows 32-bit (ILP32), confirmed by source analysis and Windows runtime reproduction. - All releases containing the 2018 guard (commit
1091975, present since well before1.0.1) with the sameunsigned long bytelenand deadLLONG_MAX > SIZE_MAXclause are expected to be affected on LLP64/ILP32; we did not audit a range we did not read. - LP64 targets (Linux, macOS, FreeBSD, Solaris, AIX) are not affected.
Vendor position
The report was handled by INCIBE-CERT, which coordinated with the vendor, and was reviewed by Redis Product Security.
- 2026-10-09 — Redis Product Security acknowledged the report, confirmed it would be studied, and rated it Medium (CVSS v4.0 6.9) — the same score Internet Security Auditors submitted.
- 2026-10-15 — hiredis development team determined that Windows is not an officially supported
platform and that the issue, being limited in scope to Windows, will not receive a code fix. The
project's security policy states that hiredis officially supports Unix-like systems (Linux, macOS,
BSD) only, that Windows is not an officially supported platform, and that there are known
incompatibilities on Windows "including security vulnerabilities"
(
https://github.com/redis/hiredis/blob/master/SECURITY.md).
Consequence. No upstream fix will be issued for the affected platforms. The defect is nonetheless real on LLP64/ILP32 (Windows) builds, as reproduced. The remediation below is therefore provided by Internet Security Auditors for consumers who ship hiredis on Windows; it has not been accepted or validated by the hiredis maintainers, and anyone applying it should review and test it against their own build.
Related advisories: Further findings from the same third-party review programme concern unrelated projects and are disclosed separately to their respective maintainers.
Solution
Note. The following is Internet Security Auditors' proposed remediation. It has not been accepted or validated by the hiredis project, which has declined to fix the issue (see Vendor position). Consumers who build hiredis for Windows can apply it to a local/patched build.
Two complementary corrections:
1. Use a size_t accumulator so the completeness size cannot truncate:
size_t bytelen; /* was: unsigned long */
2. Make the range check effective on all targets by clamping len against SIZE_MAX unconditionally (the LLONG_MAX > SIZE_MAX gate is what disables it on 64-bit):
if (len < -1 || (unsigned long long)len > (unsigned long long)SIZE_MAX) {
__redisReaderSetError(r, REDIS_ERR_PROTOCOL, "Bulk string length out of range");
return REDIS_ERR;
}With bytelen widened to size_t, bytelen += len + 2 no longer truncates and the
completeness check r->pos + bytelen <= r->len correctly rejects an under-length buffer, as it
already does on LP64.
Because no upstream fix will be issued, Windows consumers should carry the above as a local patch. Defence in depth that does not require patching hiredis: build the affected component only for LP64 targets where possible; use TLS/authenticated connections to Redis so that a network attacker cannot inject replies; and treat remote-database scanning of untrusted endpoints as a privileged operation.
Credits
This vulnerability was discovered and reported by Daniel Fernández Bleda (Internet Security Auditors).
Revision history
processBulkItem guard was introduced by commit 1091975 (2018, "Fix bulk and multi-bulk length truncation"), not by the CVE-2021-32765 fix (76a7b1000, 2021, which hardened the sibling multi-bulk path). Framing and references updated accordingly.
CVE-2026-109923 assigned by INCIBE-CERT (2026-10-13). Redis Product Security reviewed the report and rated it Medium (CVSS v4.0 6.9); the hiredis development team declined a code fix because Windows is not an officially supported platform. A Vendor position section was added and the proposed remediation is retained but explicitly marked as not accepted or validated by the project. No technical finding was changed.
Disclosure timeline
1091975); related to CVE-2021-32765. Reproduced on Windows x64 (MSVC) — 15-byte reply → 0xC0000005; confirmed absent on LP64 Linux (control + Valgrind). Confirmed the same code is present in release 1.3.0.
CVE-2026-109923.
References
CVE-2026-109923- hiredis commit
1091975"Fix bulk and multi-bulk length truncation" (2018-05-20) — introduced theprocessBulkItemguard this advisory shows is dead on LLP64 - CVE-2021-32765 / hiredis commit
76a7b1000(2021, in 1.0.1) — related fix hardening the sibling multi-bulk count path (not the bulk-string guard)
The information in this advisory is provided "as is", without warranty of any kind. Internet Security Auditors is not liable for any misuse of this information. This advisory followed a coordinated-disclosure process through INCIBE-CERT. The vendor has reviewed the report and declined to issue a code fix (see Vendor position); the remediation proposed here has not been accepted or validated by the hiredis project.
Compañía con sede en España y Colombia, especializada en auditoría de aplicaciones web, seguridad de red, test de intrusión e implantación y evaluación de cumplimiento. Nuestros clientes incluyen algunas de las mayores compañías de finanzas, telecomunicaciones, seguros y TIC. Proveedor independiente de fabricante, con experiencia profunda desde 2001. Nuestro esfuerzo en I+D incluye investigación de vulnerabilidades, colaboración en proyectos abiertos de seguridad, whitepapers y participación en eventos del sector.
¿Quieres que revisemos tu producto?
Contacta con nosotros¿Necesitas una auditoría de seguridad?
Cuéntanos tu situación y nuestro equipo se pondrá en contacto contigo para orientarte o agendar una llamada técnica.
