mirror of
https://github.com/redis/redis.git
synced 2026-02-03 20:39:54 -05:00
If `hide-user-data-from-log` config is enabled, we don't print client argv in the crashlog to avoid leaking user info. Though, debugging a crash becomes harder as we don't see the command arguments causing the crash. With this PR, we'll be printing command tokens to the log. As we have command tokens defined in json schema for each command, using this data, we can find tokens in the client argv. e.g. `SET key value GET EX 10` ---> we'll print `SET * * GET EX *` in the log. Modules should introduce their command structure via `RM_SetCommandInfo()`. Then, on a crash we'll able to know module command tokens.
311 lines
14 KiB
C
311 lines
14 KiB
C
#include "redismodule.h"
|
|
|
|
#include <strings.h>
|
|
#include <sys/mman.h>
|
|
|
|
#define UNUSED(V) ((void) V)
|
|
|
|
void assertCrash(RedisModuleInfoCtx *ctx, int for_crash_report) {
|
|
UNUSED(ctx);
|
|
UNUSED(for_crash_report);
|
|
RedisModule_Assert(0);
|
|
}
|
|
|
|
void segfaultCrash(RedisModuleInfoCtx *ctx, int for_crash_report) {
|
|
UNUSED(ctx);
|
|
UNUSED(for_crash_report);
|
|
/* Compiler gives warnings about writing to a random address
|
|
* e.g "*((char*)-1) = 'x';". As a workaround, we map a read-only area
|
|
* and try to write there to trigger segmentation fault. */
|
|
char *p = mmap(NULL, 4096, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
|
*p = 'x';
|
|
}
|
|
|
|
int cmd_crash(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
|
|
UNUSED(ctx);
|
|
UNUSED(argv);
|
|
UNUSED(argc);
|
|
|
|
RedisModule_Assert(0);
|
|
return REDISMODULE_OK;
|
|
}
|
|
|
|
int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
|
|
REDISMODULE_NOT_USED(argv);
|
|
REDISMODULE_NOT_USED(argc);
|
|
if (RedisModule_Init(ctx,"modulecrash",1,REDISMODULE_APIVER_1)
|
|
== REDISMODULE_ERR) return REDISMODULE_ERR;
|
|
|
|
if (argc >= 1) {
|
|
if (!strcasecmp(RedisModule_StringPtrLen(argv[0], NULL), "segfault")) {
|
|
if (RedisModule_RegisterInfoFunc(ctx, segfaultCrash) == REDISMODULE_ERR) return REDISMODULE_ERR;
|
|
} else if (!strcasecmp(RedisModule_StringPtrLen(argv[0], NULL),"assert")) {
|
|
if (RedisModule_RegisterInfoFunc(ctx, assertCrash) == REDISMODULE_ERR) return REDISMODULE_ERR;
|
|
}
|
|
}
|
|
|
|
/* Create modulecrash.xadd command which is similar to xadd command.
|
|
* It will crash in the command handler to verify we print command tokens
|
|
* when hide-user-data-from-log config is enabled */
|
|
RedisModuleCommandInfo info = {
|
|
.version = REDISMODULE_COMMAND_INFO_VERSION,
|
|
.arity = -5,
|
|
.key_specs = (RedisModuleCommandKeySpec[]){
|
|
{
|
|
.notes = "UPDATE instead of INSERT because of the optional trimming feature",
|
|
.flags = REDISMODULE_CMD_KEY_RW | REDISMODULE_CMD_KEY_UPDATE,
|
|
.begin_search_type = REDISMODULE_KSPEC_BS_INDEX,
|
|
.bs.index.pos = 1,
|
|
.find_keys_type = REDISMODULE_KSPEC_FK_RANGE,
|
|
.fk.range = {0,1,0}
|
|
},
|
|
{0}
|
|
},
|
|
.args = (RedisModuleCommandArg[]){
|
|
{
|
|
.name = "key",
|
|
.type = REDISMODULE_ARG_TYPE_KEY,
|
|
.key_spec_index = 0
|
|
},
|
|
{
|
|
.name = "nomkstream",
|
|
.type = REDISMODULE_ARG_TYPE_PURE_TOKEN,
|
|
.token = "NOMKSTREAM",
|
|
.since = "6.2.0",
|
|
.flags = REDISMODULE_CMD_ARG_OPTIONAL
|
|
},
|
|
{
|
|
.name = "trim",
|
|
.type = REDISMODULE_ARG_TYPE_BLOCK,
|
|
.flags = REDISMODULE_CMD_ARG_OPTIONAL,
|
|
.subargs = (RedisModuleCommandArg[]){
|
|
{
|
|
.name = "strategy",
|
|
.type = REDISMODULE_ARG_TYPE_ONEOF,
|
|
.subargs = (RedisModuleCommandArg[]){
|
|
{
|
|
.name = "maxlen",
|
|
.type = REDISMODULE_ARG_TYPE_PURE_TOKEN,
|
|
.token = "MAXLEN",
|
|
},
|
|
{
|
|
.name = "minid",
|
|
.type = REDISMODULE_ARG_TYPE_PURE_TOKEN,
|
|
.token = "MINID",
|
|
.since = "6.2.0",
|
|
},
|
|
{0}
|
|
}
|
|
},
|
|
{
|
|
.name = "operator",
|
|
.type = REDISMODULE_ARG_TYPE_ONEOF,
|
|
.flags = REDISMODULE_CMD_ARG_OPTIONAL,
|
|
.subargs = (RedisModuleCommandArg[]){
|
|
{
|
|
.name = "equal",
|
|
.type = REDISMODULE_ARG_TYPE_PURE_TOKEN,
|
|
.token = "="
|
|
},
|
|
{
|
|
.name = "approximately",
|
|
.type = REDISMODULE_ARG_TYPE_PURE_TOKEN,
|
|
.token = "~"
|
|
},
|
|
{0}
|
|
}
|
|
},
|
|
{
|
|
.name = "threshold",
|
|
.type = REDISMODULE_ARG_TYPE_STRING,
|
|
.display_text = "threshold" /* Just for coverage, doesn't have a visible effect */
|
|
},
|
|
{
|
|
.name = "count",
|
|
.type = REDISMODULE_ARG_TYPE_INTEGER,
|
|
.token = "LIMIT",
|
|
.since = "6.2.0",
|
|
.flags = REDISMODULE_CMD_ARG_OPTIONAL
|
|
},
|
|
{0}
|
|
}
|
|
},
|
|
{
|
|
.name = "id-selector",
|
|
.type = REDISMODULE_ARG_TYPE_ONEOF,
|
|
.subargs = (RedisModuleCommandArg[]){
|
|
{
|
|
.name = "auto-id",
|
|
.type = REDISMODULE_ARG_TYPE_PURE_TOKEN,
|
|
.token = "*"
|
|
},
|
|
{
|
|
.name = "id",
|
|
.type = REDISMODULE_ARG_TYPE_STRING,
|
|
},
|
|
{0}
|
|
}
|
|
},
|
|
{
|
|
.name = "data",
|
|
.type = REDISMODULE_ARG_TYPE_BLOCK,
|
|
.flags = REDISMODULE_CMD_ARG_MULTIPLE,
|
|
.subargs = (RedisModuleCommandArg[]){
|
|
{
|
|
.name = "field",
|
|
.type = REDISMODULE_ARG_TYPE_STRING,
|
|
},
|
|
{
|
|
.name = "value",
|
|
.type = REDISMODULE_ARG_TYPE_STRING,
|
|
},
|
|
{0}
|
|
}
|
|
},
|
|
{0}
|
|
}
|
|
};
|
|
|
|
RedisModuleCommand *cmd;
|
|
|
|
if (RedisModule_CreateCommand(ctx,"modulecrash.xadd", cmd_crash,"write deny-oom random fast",0,0,0) == REDISMODULE_ERR)
|
|
return REDISMODULE_ERR;
|
|
cmd = RedisModule_GetCommand(ctx,"modulecrash.xadd");
|
|
if (RedisModule_SetCommandInfo(cmd, &info) == REDISMODULE_ERR)
|
|
return REDISMODULE_ERR;
|
|
|
|
/* Create a subcommand: modulecrash.parent sub
|
|
* It will crash in the command handler to verify we print subcommand name
|
|
* when hide-user-data-from-log config is enabled */
|
|
RedisModuleCommandInfo subcommand_info = {
|
|
.version = REDISMODULE_COMMAND_INFO_VERSION,
|
|
.arity = -5,
|
|
.key_specs = (RedisModuleCommandKeySpec[]){
|
|
{
|
|
.flags = REDISMODULE_CMD_KEY_RW | REDISMODULE_CMD_KEY_UPDATE,
|
|
.begin_search_type = REDISMODULE_KSPEC_BS_INDEX,
|
|
.bs.index.pos = 1,
|
|
.find_keys_type = REDISMODULE_KSPEC_FK_RANGE,
|
|
.fk.range = {0,1,0}
|
|
},
|
|
{0}
|
|
},
|
|
.args = (RedisModuleCommandArg[]){
|
|
{
|
|
.name = "key",
|
|
.type = REDISMODULE_ARG_TYPE_KEY,
|
|
.key_spec_index = 0
|
|
},
|
|
{
|
|
.name = "token",
|
|
.type = REDISMODULE_ARG_TYPE_PURE_TOKEN,
|
|
.token = "TOKEN",
|
|
.flags = REDISMODULE_CMD_ARG_OPTIONAL
|
|
},
|
|
{
|
|
.name = "data",
|
|
.type = REDISMODULE_ARG_TYPE_BLOCK,
|
|
.subargs = (RedisModuleCommandArg[]){
|
|
{
|
|
.name = "field",
|
|
.type = REDISMODULE_ARG_TYPE_STRING,
|
|
},
|
|
{
|
|
.name = "value",
|
|
.type = REDISMODULE_ARG_TYPE_STRING,
|
|
},
|
|
{0}
|
|
}
|
|
},
|
|
{0}
|
|
}
|
|
};
|
|
|
|
if (RedisModule_CreateCommand(ctx,"modulecrash.parent",NULL,"",0,0,0) == REDISMODULE_ERR)
|
|
return REDISMODULE_ERR;
|
|
|
|
RedisModuleCommand *parent = RedisModule_GetCommand(ctx,"modulecrash.parent");
|
|
|
|
if (RedisModule_CreateSubcommand(parent,"subcmd",cmd_crash,"",0,0,0) == REDISMODULE_ERR)
|
|
return REDISMODULE_ERR;
|
|
|
|
cmd = RedisModule_GetCommand(ctx,"modulecrash.parent|subcmd");
|
|
if (RedisModule_SetCommandInfo(cmd, &subcommand_info) == REDISMODULE_ERR)
|
|
return REDISMODULE_ERR;
|
|
|
|
/* Create modulecrash.zunion command which is similar to zunion command.
|
|
* It will crash in the command handler to verify we print command tokens
|
|
* when hide-user-data-from-log config is enabled */
|
|
RedisModuleCommandInfo zunioninfo = {
|
|
.version = REDISMODULE_COMMAND_INFO_VERSION,
|
|
.arity = -5,
|
|
.key_specs = (RedisModuleCommandKeySpec[]){
|
|
{
|
|
.flags = REDISMODULE_CMD_KEY_RO,
|
|
.begin_search_type = REDISMODULE_KSPEC_BS_INDEX,
|
|
.bs.index.pos = 1,
|
|
.find_keys_type = REDISMODULE_KSPEC_FK_KEYNUM,
|
|
.fk.keynum = {0,1,1}
|
|
},
|
|
{0}
|
|
},
|
|
.args = (RedisModuleCommandArg[]){
|
|
{
|
|
.name = "numkeys",
|
|
.type = REDISMODULE_ARG_TYPE_INTEGER,
|
|
},
|
|
{
|
|
.name = "key",
|
|
.type = REDISMODULE_ARG_TYPE_KEY,
|
|
.key_spec_index = 0,
|
|
.flags = REDISMODULE_CMD_ARG_MULTIPLE
|
|
},
|
|
{
|
|
.name = "weights",
|
|
.type = REDISMODULE_ARG_TYPE_INTEGER,
|
|
.token = "WEIGHTS",
|
|
.flags = REDISMODULE_CMD_ARG_OPTIONAL | REDISMODULE_CMD_ARG_MULTIPLE
|
|
},
|
|
{
|
|
.name = "aggregate",
|
|
.type = REDISMODULE_ARG_TYPE_ONEOF,
|
|
.token = "AGGREGATE",
|
|
.flags = REDISMODULE_CMD_ARG_OPTIONAL,
|
|
.subargs = (RedisModuleCommandArg[]){
|
|
{
|
|
.name = "sum",
|
|
.type = REDISMODULE_ARG_TYPE_PURE_TOKEN,
|
|
.token = "sum"
|
|
},
|
|
{
|
|
.name = "min",
|
|
.type = REDISMODULE_ARG_TYPE_PURE_TOKEN,
|
|
.token = "min"
|
|
},
|
|
{
|
|
.name = "max",
|
|
.type = REDISMODULE_ARG_TYPE_PURE_TOKEN,
|
|
.token = "max"
|
|
},
|
|
{0}
|
|
}
|
|
},
|
|
{
|
|
.name = "withscores",
|
|
.type = REDISMODULE_ARG_TYPE_PURE_TOKEN,
|
|
.token = "WITHSCORES",
|
|
.flags = REDISMODULE_CMD_ARG_OPTIONAL
|
|
},
|
|
{0}
|
|
}
|
|
};
|
|
|
|
if (RedisModule_CreateCommand(ctx,"modulecrash.zunion", cmd_crash,"readonly",0,0,0) == REDISMODULE_ERR)
|
|
return REDISMODULE_ERR;
|
|
cmd = RedisModule_GetCommand(ctx,"modulecrash.zunion");
|
|
if (RedisModule_SetCommandInfo(cmd, &zunioninfo) == REDISMODULE_ERR)
|
|
return REDISMODULE_ERR;
|
|
|
|
|
|
return REDISMODULE_OK;
|
|
}
|