Skip to content

Crash when blocked client overflows its output buffer from a thread-safe context #5443

@swilly22

Description

@swilly22

In short, I believe the issue is early release of a blocked client which keeps on sending data from a worker thread.

To reproduce, compile and load the module below, call crash.ASYNC

#define REDISMODULE_EXPERIMENTAL_API // Required for block client. #include "redismodule.h" #include <pthread.h> #include <unistd.h> #define UNUSED(x) (void)(x) void* _Crash_ASync(void *args) { RedisModuleBlockedClient *bc = (RedisModuleBlockedClient*)args; RedisModuleCtx *ctx = RedisModule_GetThreadSafeContext(bc); RedisModule_ReplyWithArray(ctx, 999999); for(int i = 0; i < 999999; i++) { RedisModule_ReplyWithSimpleString(ctx, "Oh no!"); } RedisModule_FreeThreadSafeContext(ctx); RedisModule_UnblockClient(bc, NULL); return NULL; } int Crash_ASync(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { /* When output buffer overflows, Redis crashs when disconnect the client. */ UNUSED(argv); UNUSED(argc); RedisModuleBlockedClient *bc = RedisModule_BlockClient(ctx, NULL, NULL, NULL, 0); pthread_t thread; pthread_create(&thread, NULL, _Crash_ASync, bc); pthread_detach(thread); return REDISMODULE_OK; } int Crash_Sync(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { /* When output buffer overflows, Redis is able to * disconnect the client. */ UNUSED(argv); UNUSED(argc); RedisModule_ReplyWithArray(ctx, 999999); for(int i = 0; i < 999999; i++) { RedisModule_ReplyWithSimpleString(ctx, "No worries!"); } return REDISMODULE_OK; } void usage() { printf("\n\n\n***************************************************************************\n"); printf("This module triggers a bug caused by an early client free of a blocked-client while still sending data from a thread safe context\n"); printf("Be sure to reduce client output buffer limit by:\n"); printf("CONFIG SET client-output-buffer-limit \"normal 1 1 1 slave 1 1 1 pubsub 1 1 1\"\n"); printf("***************************************************************************\n\n\n"); } int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { UNUSED(argv); UNUSED(argc); if (RedisModule_Init(ctx, "crash", 1, REDISMODULE_APIVER_1) == REDISMODULE_ERR) { return REDISMODULE_ERR; } // Trigger crash. if(RedisModule_CreateCommand(ctx, "crash.ASYNC", Crash_ASync, "write", 1, 1, 1) == REDISMODULE_ERR) { return REDISMODULE_ERR; } // Won't trigger the crash. if(RedisModule_CreateCommand(ctx, "crash.SYNC", Crash_Sync, "write", 1, 1, 1) == REDISMODULE_ERR) { return REDISMODULE_ERR; } usage(); return REDISMODULE_OK; } 

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