From fd0ca74763f232336005b9bda889639a2bcd4a39 Mon Sep 17 00:00:00 2001 From: Yossi Gottlieb Date: Sun, 21 Nov 2021 15:54:14 +0200 Subject: [PATCH] Fix occasional RM_Call() crashes. (#9805) With dynamically growing argc (#9528), it is necessary to initialize argv_len. Normally createClient() handles that, but in the case of a module shared_client, this needs to be done explicitly. This also addresses an issue with rewriteClientCommandArgument() which doesn't properly handle the case where the new element extends beyond argc but not beyond argv_len. --- src/module.c | 22 ++++++++++++---------- src/networking.c | 13 ++++++++++--- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/module.c b/src/module.c index 2e5f367caa..3fe5c6a3e2 100644 --- a/src/module.c +++ b/src/module.c @@ -394,7 +394,7 @@ typedef struct RedisModuleKeyOptCtx { void RM_FreeCallReply(RedisModuleCallReply *reply); void RM_CloseKey(RedisModuleKey *key); void autoMemoryCollect(RedisModuleCtx *ctx); -robj **moduleCreateArgvFromUserFormat(const char *cmdname, const char *fmt, int *argcp, int *flags, va_list ap); +robj **moduleCreateArgvFromUserFormat(const char *cmdname, const char *fmt, int *argcp, int *argvlenp, int *flags, va_list ap); void moduleReplicateMultiIfNeeded(RedisModuleCtx *ctx); void RM_ZsetRangeStop(RedisModuleKey *kp); static void zsetKeyReset(RedisModuleKey *key); @@ -2400,7 +2400,7 @@ int RM_Replicate(RedisModuleCtx *ctx, const char *cmdname, const char *fmt, ...) /* Create the client and dispatch the command. */ va_start(ap, fmt); - argv = moduleCreateArgvFromUserFormat(cmdname,fmt,&argc,&flags,ap); + argv = moduleCreateArgvFromUserFormat(cmdname,fmt,&argc,NULL,&flags,ap); va_end(ap); if (argv == NULL) return REDISMODULE_ERR; @@ -4765,9 +4765,9 @@ RedisModuleString *RM_CreateStringFromCallReply(RedisModuleCallReply *reply) { } } -/* Returns an array of robj pointers, and populates *argc with the number - * of items, by parsing the format specifier "fmt" as described for - * the RM_Call(), RM_Replicate() and other module APIs. +/* Returns an array of robj pointers, by parsing the format specifier "fmt" as described for + * the RM_Call(), RM_Replicate() and other module APIs. Populates *argcp with the number of + * items and *argvlenp with the length of the allocated argv. * * The integer pointed by 'flags' is populated with flags according * to special modifiers in "fmt". For now only one exists: @@ -4781,7 +4781,7 @@ RedisModuleString *RM_CreateStringFromCallReply(RedisModuleCallReply *reply) { * * On error (format specifier error) NULL is returned and nothing is * allocated. On success the argument vector is returned. */ -robj **moduleCreateArgvFromUserFormat(const char *cmdname, const char *fmt, int *argcp, int *flags, va_list ap) { +robj **moduleCreateArgvFromUserFormat(const char *cmdname, const char *fmt, int *argcp, int *argvlenp, int *flags, va_list ap) { int argc = 0, argv_size, j; robj **argv = NULL; @@ -4847,7 +4847,8 @@ robj **moduleCreateArgvFromUserFormat(const char *cmdname, const char *fmt, int } p++; } - *argcp = argc; + if (argcp) *argcp = argc; + if (argvlenp) *argvlenp = argv_size; return argv; fmterr: @@ -4909,14 +4910,14 @@ RedisModuleCallReply *RM_Call(RedisModuleCtx *ctx, const char *cmdname, const ch struct redisCommand *cmd; client *c = NULL; robj **argv = NULL; - int argc = 0, flags = 0; + int argc = 0, argv_len = 0, flags = 0; va_list ap; RedisModuleCallReply *reply = NULL; int replicate = 0; /* Replicate this command? */ /* Handle arguments. */ va_start(ap, fmt); - argv = moduleCreateArgvFromUserFormat(cmdname,fmt,&argc,&flags,ap); + argv = moduleCreateArgvFromUserFormat(cmdname,fmt,&argc,&argv_len,&flags,ap); replicate = flags & REDISMODULE_ARGV_REPLICATE; va_end(ap); @@ -4941,6 +4942,7 @@ RedisModuleCallReply *RM_Call(RedisModuleCtx *ctx, const char *cmdname, const ch c->db = ctx->client->db; c->argv = argv; c->argc = argc; + c->argv_len = argv_len; c->resp = 2; if (flags & REDISMODULE_ARGV_RESP_3) { c->resp = 3; @@ -5974,7 +5976,7 @@ void RM_EmitAOF(RedisModuleIO *io, const char *cmdname, const char *fmt, ...) { /* Emit the arguments into the AOF in Redis protocol format. */ va_start(ap, fmt); - argv = moduleCreateArgvFromUserFormat(cmdname,fmt,&argc,&flags,ap); + argv = moduleCreateArgvFromUserFormat(cmdname,fmt,&argc,NULL,&flags,ap); va_end(ap); if (argv == NULL) { serverLog(LL_WARNING, diff --git a/src/networking.c b/src/networking.c index b33feac5c2..73ea286e9e 100644 --- a/src/networking.c +++ b/src/networking.c @@ -3276,9 +3276,16 @@ void replaceClientCommandVector(client *c, int argc, robj **argv) { void rewriteClientCommandArgument(client *c, int i, robj *newval) { robj *oldval; retainOriginalCommandVector(c); - if (i >= c->argv_len) { - c->argv = zrealloc(c->argv,sizeof(robj*)*(i+1)); - c->argc = c->argv_len = i+1; + + /* We need to handle both extending beyond argc (just update it and + * initialize the new element) or beyond argv_len (realloc is needed). + */ + if (i >= c->argc) { + if (i >= c->argv_len) { + c->argv = zrealloc(c->argv,sizeof(robj*)*(i+1)); + c->argv_len = i+1; + } + c->argc = i+1; c->argv[i] = NULL; } oldval = c->argv[i];