Skip to content

[BUG] Off-by-one global buffer overflow in RM_RegisterClusterMessageReceiver #975

Description

@BreakingBad6

There is a classic off-by-one global buffer overflow (OOB Write/Read) vulnerability in the RM_RegisterClusterMessageReceiver API provided for Redis modules.
The global array clusterReceivers is declared with a fixed size of UINT8_MAX (255 elements) in module.cpp:

// @KeyDB/src/module.cpp:6184
static moduleClusterReceiver *clusterReceivers[UINT8_MAX]; 

Since the array size is 255, the valid array indices are 0 to 254.
However, the RM_RegisterClusterMessageReceiver function accepts the type parameter as a uint8_t:

// @KeyDB/src/module.cpp:6208
void RM_RegisterClusterMessageReceiver(RedisModuleCtx *ctx, uint8_t type, RedisModuleClusterMessageReceiver callback) {
    if (!g_pserver->cluster_enabled) return;
    uint64_t module_id = moduleTypeEncodeId(ctx->module->name,0);
    moduleClusterReceiver *r = clusterReceivers[type], *prev = NULL; // <--- OOB Read

Since a uint8_t variable can hold values up to 255 (UINT8_MAX), passing type = 255 causes an exact 8-byte out-of-bounds access (clusterReceivers[255]) in the BSS segment, overriding or leaking adjacent global variables.

To reproduce

  1. Compile KeyDB with AddressSanitizer enabled:
make MALLOC=libc \
     OPTIMIZATION="-O1" \
     CFLAGS="-fsanitize=address,undefined -g -fno-omit-frame-pointer" \
     CXXFLAGS="-fsanitize=address,undefined -g -fno-omit-frame-pointer" \
     LDFLAGS="-fsanitize=address,undefined" \
     -j$(nproc)
  1. Create a minimal Redis Module to trigger the vulnerability:
#include "redismodule.h"
void MyClusterMessageReceiver(RedisModuleCtx *ctx, const char *sender_id, uint8_t type, const unsigned char *payload, uint32_t len) {}
int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
    if (RedisModule_Init(ctx, "oob_test", 1, REDISMODULE_APIVER_1) == REDISMODULE_ERR) {
        return REDISMODULE_ERR;
    }
// Pass 255 (UINT8_MAX) as the type to trigger OOB write
    RedisModule_RegisterClusterMessageReceiver(ctx, 255, MyClusterMessageReceiver);
    return REDISMODULE_OK;
}
  1. Compile the module:
gcc -shared -fPIC -o xxx.so xxx.c -I./src -DREDISMODULE_EXPERIMENTAL_API
  1. Launch KeyDB in cluster mode and load the module:
./src/keydb-server --cluster-enabled yes --loadmodule ./oob_test.so

Expected behavior

The API should properly validate the bounds of type or the array should be declared with enough space to cover the entire uint8_t range.
For example, either changing the declaration to:

static moduleClusterReceiver *clusterReceivers[UINT8_MAX + 1]; 

Or adding a bounds check in the function:

if (type >= UINT8_MAX) return; 

Additional information

Image

The ASAN trace perfectly confirms the 8-byte out-of-bounds read/write on the global .bss segment immediately following the clusterReceivers array boundary.
We are fully aware that exploiting this bug requires the attacker to have the privilege to load a Redis module or leverage an existing trusted module's API that improperly passes user-controlled integers to this function. Therefore, its real-world exploitability is highly restricted. However, it remains a valid and concrete memory corruption flaw in the codebase.
Could you please help assess if this qualifies for a CVE assignment? If so, I would appreciate it if the credit/attribution could be given to Yanzhao Shen. Thanks!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions