c++ hiredis rediscommand

2018. 7. 18. 14:36Programming/Redis

c++ hiredis rediscommand

#0 0x00007f344c6f9428 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:54
#1 0x00007f344c6fb02a in __GI_abort () at abort.c:89
#2 0x00007f344c73b7ea in __libc_message (do_abort=2, fmt=fmt@entry=0x7f344c854ed8 "*** Error in `%s': %s: 0x%s ***\n") at ../sysdeps/posix/libc_fatal.c:175
#3 0x00007f344c747781 in malloc_printerr (ar_ptr=0x7f3140000020, ptr=0x7f31400042b0, str=0x7f344c851c75 "corrupted size vs. prev_size", action=<optimized out>) at malloc.c:5006
#4 _int_realloc (av=av@entry=0x7f3140000020, oldp=oldp@entry=0x7f3140004290, oldsize=oldsize@entry=32, nb=nb@entry=80) at malloc.c:4298
#5 0x00007f344c748839 in __GI___libc_realloc (oldmem=oldmem@entry=0x7f31400042a0, bytes=bytes@entry=66) at malloc.c:3045
#6 0x0000000000944add in sdsMakeRoomFor (s=0x7f31400042a3 "", addlen=addlen@entry=31) at sds.c:221
#7 0x0000000000945463 in sdscatlen (s=<optimized out>, t=0x7f31400ce5f0, len=31) at sds.c:379
#8 0x0000000000944240 in __redisAppendCommand (c=0x7f31400ab660, cmd=<optimized out>, len=<optimized out>) at hiredis.c:910
#9 0x00000000009442cc in redisvAppendCommand (c=0x7f31400ab660, format=<optimized out>, ap=<optimized out>) at hiredis.c:942
#10 0x00000000009444cd in redisvCommand (c=0x7f31400ab660, format=<optimized out>, ap=<optimized out>) at hiredis.c:1003


라이브중 redisvCommand에서 Append 하는 상황에서 서버가 떨어지는 상황 발생

원인은 redisvCommand -> redisvAppendCommand 하는 중 가변인자로 명령어 조합하는 중에 메모리 재할당 과정에서 메모리를 잘못 할당될 가능성이 있다고 판단함.

그렇기 때문에 redisvCommand가 아닌 redisCommand로 Command 문자열을 sprintf로 조합하여 전송하는 방식으로 수정

va_list ap;

va_start(ap, fmt);

int len = vsnprintf(nullptr, 0, fmt, ap);

va_end(ap);


va_start(ap, fmt);

char* replycommand = new char[len + 1];

vsprintf(replycommand, fmt, ap);

va_end(ap);

reply = (redisReply*)redisCommand(m_redisContext, replycommand);

delete[] replycommand;


가변인자로 들어온 인자에 대해서 문자열을 동적할당 하여 받아주려면 linux버전에서는 _vscprintf를 사용할 수 없음.

그렇기 때문에 비슷한 기능을 하는 gcc에서 사용 가능한 vsnprintf 함수를 사용하여 함수의 리턴값에서 문자열의 길이를 반환하여 길이 측정 후 동적 할당 하도록 함


여기서 다시 문제가 생김


static const char* ranklua = "local score = redis.call('ZSCORE', ARGV[1], ARGV[2]);"

"if (score == false) then "

" return {0, -1}; "

"end;"

"local rank = redis.call('ZCOUNT', ARGV[1], '('..score, '+inf');"

"return {tonumber(score), tonumber(rank)};";


REPLY reply = GetReply("EVAL %s 0 SRank:%d %llu", ranklua, season, guildSequence);


이런식의 lua를 %s 인자 형태로 넘기게 될 경우에 모든 command라인을 명령어로 인식함. 결국 rollback


int redisvAppendCommand(redisContext *c, const char *format, va_list ap) {

    char *cmd;

    int len;


    len = redisvFormatCommand(&cmd,format,ap);

    if (len == -1) {

        __redisSetError(c,REDIS_ERR_OOM,"Out of memory");

        return REDIS_ERR;

    } else if (len == -2) {

        __redisSetError(c,REDIS_ERR_OTHER,"Invalid format string");

        return REDIS_ERR;

    }


    if (__redisAppendCommand(c,cmd,len) != REDIS_OK) {

        free(cmd);

        return REDIS_ERR;

    }


    free(cmd);

    return REDIS_OK;

}


int redisAppendCommand(redisContext *c, const char *format, ...) {

    va_list ap;

    int ret;


    va_start(ap,format);

    ret = redisvAppendCommand(c,format,ap);

    va_end(ap);

    return ret;

}


redisAppendCommand에서 redisvAppendCommand함수를 호출하여 커맨드 조합하므로 수정하나 안하나 동일한 문제가 발생함.


// 수정 후 redis monitor에 잡힌 command

"EVAL" "local" "score" "=" "redis.call('ZSCORE'," "ARGV[1]," "ARGV[2]);if" "(score" "==" "false)" "then" "return" "{0," "-1};" "end;local" "rank" "=" "redis.call('ZCOUNT'," "ARGV[1]," "'('..score," "'+inf');return" "{tonumber(score)," "tonumber(rank)};" "0" "SRank:2" "34"


// 기존 redis monitor에 잡힌 command

 "EVAL" "local score = redis.call('ZSCORE', ARGV[1], ARGV[2]);if (score == false) then  return {0, -1}; end;local rank = redis.call('ZCOUNT', ARGV[1], '('..score, '+inf');return {tonumber(score), tonumber(rank)};" "0" "SRank:2" "34"


결론 1. linux는 windows에서 사용하는 가변인자의 길이를 측정할 함수를 사용할 수 없으므로 linux버전에 맞게 튜닝해야됨

결론 2. redisvCommand 함수를 사용할 경우 간헐적으로 메모리가 잘못될 수 있음

결론 3. redisvCommand를 redisCommand로 변경할 경우 luascript로 명령어를 실행하지 않는 반경에서 수정 가능함.

결론 4. luascript를 사용 할 경우, 메모리가 터지지 않도록 운에 맡기자..

'Programming > Redis' 카테고리의 다른 글

[Redis] Hiredis vs cpp_redis  (0) 2019.04.29
레디스 개발자 antirez 블로그 주소  (0) 2018.08.27
keys와 scan 풀탐색 시간 비교  (0) 2018.08.02