Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new module API flag to bypass command validation #1357

Merged
merged 2 commits into from
Feb 20, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 34 additions & 12 deletions src/module.c
Original file line number Diff line number Diff line change
Expand Up @@ -2566,11 +2566,27 @@ void VM_Yield(ValkeyModuleCtx *ctx, int flags, const char *busy_reply) {
* By default, the server will not fire key-space notifications that happened inside
* a key-space notification callback. This flag allows to change this behavior
* and fire nested key-space notifications. Notice: if enabled, the module
* should protected itself from infinite recursion. */
* should protected itself from infinite recursion.
*
* VALKEYMODULE_OPTIONS_SKIP_COMMAND_VALIDATION:
* When set, this option allows the module to skip command validation.
* This is useful in scenarios where the module needs to bypass
* command validation (lookupCommandByCString) for specific operations
* to reduce overhead or handle trusted custom command logic. */
void VM_SetModuleOptions(ValkeyModuleCtx *ctx, int options) {
ctx->module->options = options;
}

/* Add specified options to the module's current options. */
void VM_AddModuleOptions(ValkeyModuleCtx *ctx, int options) {
ctx->module->options |= options;
}

/* Remove specified options from the module's current options. */
void VM_RemoveModuleOptions(ValkeyModuleCtx *ctx, int options) {
ctx->module->options &= ~options;
}

/* Signals that the key is modified from user's perspective (i.e. invalidate WATCH
* and client side caching).
*
Expand Down Expand Up @@ -3630,8 +3646,10 @@ int VM_Replicate(ValkeyModuleCtx *ctx, const char *cmdname, const char *fmt, ...
int argc = 0, flags = 0, j;
va_list ap;

cmd = lookupCommandByCString((char *)cmdname);
if (!cmd) return VALKEYMODULE_ERR;
if (!ctx->module || !(ctx->module->options & VALKEYMODULE_OPTIONS_SKIP_COMMAND_VALIDATION)) {
cmd = lookupCommandByCString((char *)cmdname);
if (!cmd) return VALKEYMODULE_ERR;
}

/* Create the client and dispatch the command. */
va_start(ap, fmt);
Expand Down Expand Up @@ -7535,15 +7553,17 @@ void VM_EmitAOF(ValkeyModuleIO *io, const char *cmdname, const char *fmt, ...) {
int argc = 0, flags = 0, j;
va_list ap;

cmd = lookupCommandByCString((char *)cmdname);
if (!cmd) {
serverLog(LL_WARNING,
"Fatal: AOF method for module data type '%s' tried to "
"emit unknown command '%s'",
io->type->name, cmdname);
io->error = 1;
errno = EINVAL;
return;
if (!io->ctx || !io->ctx->module || !(io->ctx->module->options & VALKEYMODULE_OPTIONS_SKIP_COMMAND_VALIDATION)) {
cmd = lookupCommandByCString((char *)cmdname);
if (!cmd) {
serverLog(LL_WARNING,
"Fatal: AOF method for module data type '%s' tried to "
"emit unknown command '%s'",
io->type->name, cmdname);
io->error = 1;
errno = EINVAL;
return;
}
}

/* Emit the arguments into the AOF in RESP format. */
Expand Down Expand Up @@ -13892,6 +13912,8 @@ void moduleRegisterCoreAPI(void) {
REGISTER_API(ModuleTypeGetValue);
REGISTER_API(IsIOError);
REGISTER_API(SetModuleOptions);
REGISTER_API(AddModuleOptions);
REGISTER_API(RemoveModuleOptions);
REGISTER_API(SignalModifiedKey);
REGISTER_API(SaveUnsigned);
REGISTER_API(LoadUnsigned);
Expand Down
11 changes: 10 additions & 1 deletion src/valkeymodule.h
Original file line number Diff line number Diff line change
Expand Up @@ -325,10 +325,15 @@ typedef uint64_t ValkeyModuleTimerID;
* If enabled, the module is responsible to break endless loop. */
#define VALKEYMODULE_OPTIONS_ALLOW_NESTED_KEYSPACE_NOTIFICATIONS (1 << 3)

/* Skipping command validation can improve performance by reducing the overhead associated
* with command checking, especially in high-throughput scenarios where commands
* are already pre-validated or trusted. */
#define VALKEYMODULE_OPTIONS_SKIP_COMMAND_VALIDATION (1 << 4)

/* Next option flag, must be updated when adding new module flags above!
* This flag should not be used directly by the module.
* Use ValkeyModule_GetModuleOptionsAll instead. */
#define _VALKEYMODULE_OPTIONS_FLAGS_NEXT (1 << 4)
#define _VALKEYMODULE_OPTIONS_FLAGS_NEXT (1 << 5)

/* Definitions for ValkeyModule_SetCommandInfo. */

Expand Down Expand Up @@ -1424,6 +1429,8 @@ VALKEYMODULE_API ValkeyModuleType *(*ValkeyModule_ModuleTypeGetType)(ValkeyModul
VALKEYMODULE_API void *(*ValkeyModule_ModuleTypeGetValue)(ValkeyModuleKey *key)VALKEYMODULE_ATTR;
VALKEYMODULE_API int (*ValkeyModule_IsIOError)(ValkeyModuleIO *io) VALKEYMODULE_ATTR;
VALKEYMODULE_API void (*ValkeyModule_SetModuleOptions)(ValkeyModuleCtx *ctx, int options) VALKEYMODULE_ATTR;
VALKEYMODULE_API void (*ValkeyModule_AddModuleOptions)(ValkeyModuleCtx *ctx, int options) VALKEYMODULE_ATTR;
VALKEYMODULE_API void (*ValkeyModule_RemoveModuleOptions)(ValkeyModuleCtx *ctx, int options) VALKEYMODULE_ATTR;
VALKEYMODULE_API int (*ValkeyModule_SignalModifiedKey)(ValkeyModuleCtx *ctx,
ValkeyModuleString *keyname) VALKEYMODULE_ATTR;
VALKEYMODULE_API void (*ValkeyModule_SaveUnsigned)(ValkeyModuleIO *io, uint64_t value) VALKEYMODULE_ATTR;
Expand Down Expand Up @@ -2034,6 +2041,8 @@ static int ValkeyModule_Init(ValkeyModuleCtx *ctx, const char *name, int ver, in
VALKEYMODULE_GET_API(ModuleTypeGetValue);
VALKEYMODULE_GET_API(IsIOError);
VALKEYMODULE_GET_API(SetModuleOptions);
VALKEYMODULE_GET_API(RemoveModuleOptions);
VALKEYMODULE_GET_API(AddModuleOptions);
VALKEYMODULE_GET_API(SignalModifiedKey);
VALKEYMODULE_GET_API(SaveUnsigned);
VALKEYMODULE_GET_API(LoadUnsigned);
Expand Down
2 changes: 2 additions & 0 deletions tests/modules/datatype2.c
Original file line number Diff line number Diff line change
Expand Up @@ -703,6 +703,8 @@ int ValkeyModule_OnLoad(ValkeyModuleCtx *ctx, ValkeyModuleString **argv, int arg
return VALKEYMODULE_ERR;
}

ValkeyModule_AddModuleOptions(ctx, VALKEYMODULE_OPTIONS_SKIP_COMMAND_VALIDATION);

if (ValkeyModule_CreateCommand(ctx, "mem.alloc", MemAlloc_RedisCommand, "write deny-oom", 1, 1, 1) == VALKEYMODULE_ERR) {
return VALKEYMODULE_ERR;
}
Expand Down
4 changes: 4 additions & 0 deletions tests/modules/propagate.c
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,9 @@ int propagateTestSimpleCommand(ValkeyModuleCtx *ctx, ValkeyModuleString **argv,

/* Replicate two commands to test MULTI/EXEC wrapping. */
ValkeyModule_Replicate(ctx,"INCR","c","counter-1");
ValkeyModule_AddModuleOptions(ctx, VALKEYMODULE_OPTIONS_SKIP_COMMAND_VALIDATION);
ValkeyModule_Replicate(ctx,"INCR","c","counter-2");
ValkeyModule_RemoveModuleOptions(ctx, VALKEYMODULE_OPTIONS_SKIP_COMMAND_VALIDATION);
ValkeyModule_ReplyWithSimpleString(ctx,"OK");
return VALKEYMODULE_OK;
}
Expand All @@ -266,7 +268,9 @@ int propagateTestMixedCommand(ValkeyModuleCtx *ctx, ValkeyModuleString **argv, i
ValkeyModule_FreeCallReply(reply);

ValkeyModule_Replicate(ctx,"INCR","c","counter-1");
ValkeyModule_AddModuleOptions(ctx, VALKEYMODULE_OPTIONS_SKIP_COMMAND_VALIDATION);
ValkeyModule_Replicate(ctx,"INCR","c","counter-2");
ValkeyModule_RemoveModuleOptions(ctx, VALKEYMODULE_OPTIONS_SKIP_COMMAND_VALIDATION);

reply = ValkeyModule_Call(ctx, "INCR", "c!", "after-call");
ValkeyModule_FreeCallReply(reply);
Expand Down
Loading