Summary
KeyDB stores module cluster-message receivers in static moduleClusterReceiver *clusterReceivers[UINT8_MAX]; (src/module.cpp:6184) — an array of 255 elements (indices 0..254). The cluster message type is a uint8_t (0..255). Both the register path (RM_RegisterClusterMessageReceiver) and the dispatch path (moduleCallClusterReceivers) index clusterReceivers[type] with the full 0..255 range, so type 255 reads/writes one element past the array — an out-of-bounds access on a global buffer. Cluster-bus reachable via a CLUSTERMSG_TYPE_MODULE packet with type 255 from a known peer → node crash (DoS).
Affected version
KeyDB @603ebb27fb (2024-04-04, dev "255.255.255"). KeyDB is frozen at ~2024 and never took the Valkey fix (#4410). This is the same off-by-one as the ASan-confirmed Redis finding (Valkey fixed both forks; KeyDB tracked neither).
Technical details
// src/module.cpp:6184
static moduleClusterReceiver *clusterReceivers[UINT8_MAX]; // 255 elements, idx 0..254
// dispatch (module.cpp:6188) -> clusterReceivers[type] (type up to 255)
// register (module.cpp:6212,6240)-> clusterReceivers[type] (type up to 255)
type == 255 indexes clusterReceivers[255], one past the end.
Proof of concept
A minimal module (POC/oobtest.c) registers a receiver for type 255:
RedisModule_RegisterClusterMessageReceiver(ctx, 255, dummy_cb);
Loaded on a cluster-enabled ASan build:
keydb-server --port 7711 --cluster-enabled yes --loadmodule ./oobtest.so
AddressSanitizer (evidence/asan-global-buffer-overflow.txt):
ERROR: AddressSanitizer: global-buffer-overflow ... at RM_RegisterClusterMessageReceiver module.cpp:6212
0x... is located 0 bytes to the right of global variable 'clusterReceivers'
defined in 'module.cpp:6184:31' of size 2040 (2040 = 255 * 8)
SUMMARY: AddressSanitizer: global-buffer-overflow module.cpp:6212
Attacker-reachable variant: the cluster bus reads type straight off the wire and passes it to moduleCallClusterReceivers → clusterReceivers[255] OOB read of a pointer then walked as a receiver list → crash / potential further deref.
Impact
CVSS 6.5 (AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H) — DoS (crash) of a cluster node. Escalation beyond DoS depends on the static memory adjacent to the array.
Remediation
clusterReceivers[UINT8_MAX + 1] (256 elements) and audit the unregister loop bound (mirror Valkey #4410).
Build / repro
make SANITIZE=address MALLOC=libc BUILD_TLS=no NO_MOTD=yes → src/keydb-server (ASan). gcc -I src -fPIC -shared -std=c99 oobtest.c -o oobtest.so (needs #define REDISMODULE_EXPERIMENTAL_API).
Summary
KeyDB stores module cluster-message receivers in static moduleClusterReceiver *clusterReceivers[UINT8_MAX]; (src/module.cpp:6184) — an array of 255 elements (indices 0..254). The cluster message type is a uint8_t (0..255). Both the register path (RM_RegisterClusterMessageReceiver) and the dispatch path (moduleCallClusterReceivers) index clusterReceivers[type] with the full 0..255 range, so type 255 reads/writes one element past the array — an out-of-bounds access on a global buffer. Cluster-bus reachable via a CLUSTERMSG_TYPE_MODULE packet with type 255 from a known peer → node crash (DoS).
Affected version
KeyDB @603ebb27fb (2024-04-04, dev "255.255.255"). KeyDB is frozen at ~2024 and never took the Valkey fix (#4410). This is the same off-by-one as the ASan-confirmed Redis finding (Valkey fixed both forks; KeyDB tracked neither).
Technical details
// src/module.cpp:6184
static moduleClusterReceiver *clusterReceivers[UINT8_MAX]; // 255 elements, idx 0..254
// dispatch (module.cpp:6188) -> clusterReceivers[type] (type up to 255)
// register (module.cpp:6212,6240)-> clusterReceivers[type] (type up to 255)
type == 255 indexes clusterReceivers[255], one past the end.
Proof of concept
A minimal module (POC/oobtest.c) registers a receiver for type 255:
RedisModule_RegisterClusterMessageReceiver(ctx, 255, dummy_cb);
Loaded on a cluster-enabled ASan build:
keydb-server --port 7711 --cluster-enabled yes --loadmodule ./oobtest.so
AddressSanitizer (evidence/asan-global-buffer-overflow.txt):
ERROR: AddressSanitizer: global-buffer-overflow ... at RM_RegisterClusterMessageReceiver module.cpp:6212
0x... is located 0 bytes to the right of global variable 'clusterReceivers'
defined in 'module.cpp:6184:31' of size 2040 (2040 = 255 * 8)
SUMMARY: AddressSanitizer: global-buffer-overflow module.cpp:6212
Attacker-reachable variant: the cluster bus reads type straight off the wire and passes it to moduleCallClusterReceivers → clusterReceivers[255] OOB read of a pointer then walked as a receiver list → crash / potential further deref.
Impact
CVSS 6.5 (AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H) — DoS (crash) of a cluster node. Escalation beyond DoS depends on the static memory adjacent to the array.
Remediation
clusterReceivers[UINT8_MAX + 1] (256 elements) and audit the unregister loop bound (mirror Valkey #4410).
Build / repro
make SANITIZE=address MALLOC=libc BUILD_TLS=no NO_MOTD=yes → src/keydb-server (ASan). gcc -I src -fPIC -shared -std=c99 oobtest.c -o oobtest.so (needs #define REDISMODULE_EXPERIMENTAL_API).