Submit #897659: Valkey Project Valkey 9.1.0 Out-of-Bounds Readinfo

TitleValkey Project Valkey 9.1.0 Out-of-Bounds Read
DescriptionI discovered a memory-safety vulnerability in Valkey 9.1.0 that affects the official stock server during startup RDB loading in cluster mode. The issue is triggered when valkey-server is started with cluster-enabled yes and loads a crafted dump.rdb containing a malicious RDB_OPCODE_SLOT_IMPORT record. The vulnerable code path is part of the normal startup persistence loading chain of the official src/valkey-server binary and does not require any external module, private harness, or direct invocation of internal helper functions. The bug is caused by a trust-boundary failure in the slot-import RDB loader combined with incomplete index validation in the kvstore layer. More specifically, clusterRDBLoadSlotImport() reads attacker-controlled slot bounds from the RDB as uint64_t values, but then stores them into slotRange fields that are typed as signed int. After that, the code restores importing state across databases and directly propagates those values into kvstore indexing logic. At the kvstore layer, only the upper bound is checked. No lower-bound validation is performed before the value is used as an array index. This allows a crafted slot value to become a negative index and reach kvs->hashtables[didx], leading to an out-of-bounds access and process crash. The affected execution chain is the following. During startup, main() calls loadDataFromDisk(), which enters the RDB loading path. When the loader encounters RDB_OPCODE_SLOT_IMPORT in rdb.c, it dispatches into clusterRDBLoadSlotImport() in cluster_migrateslots.c. That function loads the job name, reads the number of slot ranges, and then reads each range as uint64_t start_slot and uint64_t end_slot values. These untrusted values are then assigned into the in-memory slotRange structure, whose members are signed int start_slot and int end_slot. No validation is performed to re-establish the invariant that a cluster slot must be within the legal slot range before those values are stored. A slot import job is then created, and createSlotImportJob() immediately calls setSlotImportingStateInAllDbs(). That function iterates over the imported ranges and passes each slot into setSlotImportingStateInDb(), which in turn passes the slot directly to kvstoreSetIsImporting(). Finally, kvstoreSetIsImporting() calls kvstoreGetHashtable(), where the negative value is used as an array index into kvs->hashtables. The core root cause is therefore not a generic malformed-file handling failure, but a specific combination of numeric narrowing and missing lower-bound index validation. The RDB loader accepts slot values as wide unsigned integers and stores them into narrower signed fields without validating that the values are valid cluster slot identifiers. Once the value has entered the slotRange structure, subsequent code treats it as a normal slot index and propagates it into the kvstore indexing path. At that point, kvstoreSetIsImporting() checks only that didx is less than kvs->num_hashtables. It does not verify that didx is non-negative. As a result, a large negative value trivially satisfies the upper-bound assertion and is then used directly by kvstoreGetHashtable(). I verified this with a crafted dump.rdb in which the slot import record contained start_slot = end_slot = 0x80000000. On my test platform, the RDB loader first read this value successfully as the uint64_t number 2147483648. I then used GDB breakpoints at the assignment sites and at the kvstore indexing sites to trace the transformation of the value through the real startup path of the official server. At the point where clusterRDBLoadSlotImport() had already parsed the slot range but had not yet stored it into slotRange, the value was still 2147483648. Immediately after the assignment into slot_range->start_slot, the value became -2147483648 on the tested target platform. This showed that the attacker-controlled slot value was successfully accepted by the RDB parser, but became a negative signed slot index when stored into the signed int destination field. I then confirmed that this negative value propagated intact through the rest of the slot-import recovery path. By the time execution reached kvstoreSetIsImporting(), the didx parameter was -2147483648 while kvs->num_hashtables was 16384. The current assertion checked only didx < kvs->num_hashtables, which evaluated as true because the value was far below the upper bound. The address computation for &kvs->hashtables[didx] already showed a large negative pointer displacement, corresponding to a huge backward out-of-bounds offset. Execution then continued into kvstoreGetHashtable(), which performed return kvs->hashtables[didx] and triggered the fault. This makes the bug a real array index violation in the core server data path, not a speculative or merely theoretical issue. The crash was reproduced with the official valkey-server binary built from Valkey 9.1.0 using AddressSanitizer. To prepare a realistic input, I first generated a clean baseline RDB template using the official server and official client, then appended a crafted RDB_OPCODE_SLOT_IMPORT record and recomputed the final CRC64 so that the resulting dump.rdb was accepted by the loader as a syntactically valid RDB file. The vulnerable server was then started in cluster mode with that malicious dump.rdb configured as the startup database file. The server crashed during startup before it could finish normal service initialization. The observed runtime behavior was consistent across the full trace. The terminal output first reported that Valkey crashed with signal 11. The relevant stack showed kvstoreGetHashtable at src/kvstore.c:107, followed by kvstoreSetIsImporting, setSlotImportingStateInDb, setSlotImportingStateInAllDbs, createSlotImportJob, clusterRDBLoadSlotImport, rdbLoadRioWithLoadingCtx, rdbLoad, loadDataFromDisk, and main. A later AddressSanitizer unknown-crash report also appeared, but this happened because Valkey’s built-in crash handler performs additional memory testing after the original fault. In other words, the later ASan output was secondary crash-report noise, while the primary vulnerability trigger was the negative-index out-of-bounds access in kvstoreGetHashtable(). From a security perspective, the most defensible confirmed impact is a high-confidence server-side denial of service. The vulnerability exists in the official startup loading path of the stock server. It can be triggered by supplying a crafted RDB file that the server loads on startup in cluster mode. No module support is needed, no private test harness is involved, and the bug does not depend on calling internal helpers outside normal server execution. The current evidence supports an out-of-bounds access and crash of the core server process. I am not claiming code execution based on the currently verified results. The demonstrated impact is reliable process termination during startup. Because the bug is rooted in attacker-controlled metadata being transformed into a core array index, the issue is more serious than a simple parser rejection or generic malformed-file error. In summary, Valkey 9.1.0 contains a startup RDB loading vulnerability in the slot import subsystem. A crafted RDB_OPCODE_SLOT_IMPORT entry can carry attacker-controlled slot bounds that are parsed as uint64_t and stored unchecked into signed int slotRange fields. On the tested platform, a crafted value such as 0x80000000 becomes -2147483648. That negative value is then propagated through createSlotImportJob() and setSlotImportingStateInAllDbs() into kvstoreSetIsImporting(), where incomplete bounds checking allows it to reach kvstoreGetHashtable(). The final result is a negative-index out-of-bounds access in the official valkey-server process and a reliable server crash during startup. I discovered and reproduced this issue against the official Valkey 9.1.0 server binary and confirmed the root cause through both sanitizer output and live GDB analysis of the real startup loading path.
Source⚠️ https://github.com/valkey-io/valkey/issues/4222
User
 VULL (UID 98817)
Submission07/20/2026 18:13 (2 months ago)
Moderation09/06/2026 06:08 (2 months later)
StatusAccepted
VulDB entry399380 [valkey-io valkey up to 9.0.5/9.1.1 src/kvstore.c kvstoreGetHashtable didx out-of-bounds]
Points20

Do you need the next level of professionalism?

Upgrade your account now!