#973·KeyDB

[Security] KeyDB: invalid memory access in RESTORE with malformed zipmap (CVE-2026-25243)

Author: vulgraphCreated May 18, 2026Updated May 18, 2026

Summary

KeyDB inherits a vulnerability from its Redis 6.x codebase in src/zipmap.czipmapValidateIntegrity() — that was fixed in Redis/Valkey upstream (CVE-2026-25243, valkey-io/valkey@fea0b4064c, 2026-05-05). A crafted RESTORE command with a malformed zipmap payload can cause an out-of-bounds memory access in KeyDB.

Vulnerability Details

  • CVE: CVE-2026-25243
  • Upstream fix (Valkey): commit fea0b4064c (2026-05-05)
  • Affected file: src/zipmap.czipmapValidateIntegrity()
  • CWE: CWE-125 — Out-of-bounds Read

Root Cause

zipmapValidateIntegrity() and zipmapNext() use different methods to advance pointers for length-encoded fields:

  • zipmapValidateIntegrity() uses zipmapGetEncodedLengthSize(p) → returns 5 for the 0xFE (254) prefix regardless of the actual decoded length
  • zipmapNext() uses ZIPMAP_LEN_BYTES(decoded_len) → returns 1 when decoded_len < 254

A crafted zipmap can use the overlong 5-byte encoding (0xFE prefix + 4-byte LE value) for a length that fits in 1 byte (< 254). zipmapValidateIntegrity() accepts this (advances 5 bytes correctly), but when zipmapNext() later processes the same structure using ZIPMAP_LEN_BYTES(decoded_len), it advances only 1 byte — causing a 4-byte pointer misalignment and out-of-bounds read during hash conversion.

c
/* src/zipmap.c — zipmapValidateIntegrity() — KeyDB HEAD (2024-04-04) */
s = zipmapGetEncodedLengthSize(p);   /* 5 for 0xFE prefix */
/* ... */
l = zipmapDecodeLength(p);           /* decoded value < 254 */
/* MISSING: if (l < ZIPMAP_BIGLEN && s != 1) return 0; */
p += s;  /* advances 5 bytes — correct for encoded form */
p += l;  /* skips field — correct */

/* zipmapNext() later advances ZIPMAP_LEN_BYTES(l) = 1 byte — MISMATCH */

The attack path is via RESTORE: src/rdb.cpp:2142 calls zipmapValidateIntegrity(encoded, encoded_len, 1) during hash type loading, and then proceeds to iterate the zipmap via zipmapNext() — triggering the mismatch.

Suggested Fix

Apply the same fix as Valkey commit fea0b4064c — add a sanity check in zipmapValidateIntegrity() to reject overlong encodings for small values:

c
/* After reading field name length */
l = zipmapDecodeLength(p);
/* Sanity check: length < 254 must be encoded in 1 byte, not 5 bytes */
if (l < ZIPMAP_BIGLEN && s != 1)
    return 0;
p += s;
p += l;

/* After reading value length */
l = zipmapDecodeLength(p);
/* Sanity check: length < 254 must be encoded in 1 byte, not 5 bytes */
if (l < ZIPMAP_BIGLEN && s != 1)
    return 0;

References