转义字符导致hiredis异常

背景

C++ 在使用redis时使用的是hiredis库,在项目中碰到一个问题,某天钉钉提示服务catch error,于是就分析保存的dump

dump 路径

链接:https://pan.baidu.com/s/1CdhOVKwtP5fabTyab8UTDw
提取码:8ull

分析过程

.ecxr

加载符号文件,然后‘.ecxr’ ‘kv’

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
0:018> .ecxr
*** WARNING: Unable to verify checksum for actiksvr.exe
eax=00000005 ebx=004f7228 ecx=00000001 edx=00000005 esi=e298348b edi=1002002b
eip=7457211c esp=062ff8a8 ebp=062ff8d4 iopl=0 nv up ei pl nz na po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010202
msvcr120!TrailDownVec+0x14f:
7457211c 8a16 mov dl,byte ptr [esi] ds:002b:e298348b=??
0:018> kv
*** Stack trace for last set context - .thread/.cxr resets it
ChildEBP RetAddr Args to Child
062ff8b4 011a36d6 1002002b e298348b 004f7228 msvcr120!TrailDownVec+0x14f (FPO: [3,0,2]) [f:\dd\vctools\crt\crtw32\string\i386\memcpy.asm @ 844]
062ff8d4 011a2da2 00000003 e298348b 004f7228 actiksvr!sdscatlen+0x56
062ff92c 011a2ae3 062ff940 0bf7d158 062ff968 actiksvr!redisvFormatCommand+0x242
062ff944 011a2472 0052ce80 0bf7d158 062ff968 actiksvr!redisvAppendCommand+0x13
062ff958 01181e31 0052ce80 0bf7d158 e298348b actiksvr!redisAppendCommand+0x12
062ffa48 011820ca 0c6c2335 062ffa5c e2983443 actiksvr!CRedisMaster::SetPlayerInfo+0x1a1 (FPO: [Non-Fpo]) (CONV: thiscall) [d:\jenkins\workspace\publish_gamechannel\actiksvr\redismaster.cpp @ 71]
062ffa80 01189135 0c6c2335 062ffb00 e298327f actiksvr!CRedisMaster::SetPlayerInfo+0x6a (FPO: [Non-Fpo]) (CONV: thiscall) [d:\jenkins\workspace\publish_gamechannel\actiksvr\redismaster.cpp @ 42]
062ffcbc 0118975a 07660e78 0f2f9490 03dfa5d0 actiksvr!CSockServer::OnLogon+0x215 (FPO: [Non-Fpo]) (CONV: thiscall) [d:\jenkins\workspace\publish_gamechannel\actiksvr\socksvr.cpp @ 553]
062ffd0c 0118eb84 07660e78 0f2f9490 001df010 actiksvr!CSockServer::OnRequest+0x2aa (FPO: [Non-Fpo]) (CONV: thiscall) [d:\jenkins\workspace\publish_gamechannel\actiksvr\socksvr.cpp @ 227]
062ffd34 0119367d 00000000 04d611d0 03db0be0 actiksvr!CIocpWorker::DoWorkLoop+0xa4
062ffd4c 0119364b 062ffd8c 7458c01d 001df008 actiksvr!CBaseWorker::WorkerThreadProc+0x2d
062ffd54 7458c01d 001df008 53e9f2fa 00000000 actiksvr!CBaseWorker::WorkerThreadFunc+0xb
062ffd8c 7458c001 00000000 062ffda4 7702336a msvcr120!_callthreadstartex+0x1b (FPO: [Non-Fpo]) (CONV: cdecl) [f:\dd\vctools\crt\crtw32\startup\threadex.c @ 376]
062ffd98 7702336a 03db0be0 062ffde4 77559902 msvcr120!_threadstartex+0x7c (FPO: [Non-Fpo]) (CONV: stdcall) [f:\dd\vctools\crt\crtw32\startup\threadex.c @ 354]
062ffda4 77559902 03db0be0 c036ffd3 00000000 kernel32!BaseThreadInitThunk+0xe (FPO: [Non-Fpo])
062ffde4 775598d5 7458bfb4 03db0be0 ffffffff ntdll!__RtlUserThreadStart+0x70 (FPO: [Non-Fpo])
062ffdfc 00000000 7458bfb4 03db0be0 00000000 ntdll!_RtlUserThreadStart+0x1b (FPO: [Non-Fpo])

因为有类似经验(看过hiredis解析代码),猜测应该就是有特殊字符(比如 % 等)导致

查看参数

通过 ‘local’ 查看CSockServer::OnLogon 对应代码的参数,发现 其中imei 字符串中包含了 ‘%’ ,然后再编写demo验证,问题必现。

解决方法

知道原因,解决方法就很多了,

  • 可以用标准的redis方法void redisCommand(redisContext c, const char *format, …);
  • 我使用的是替换法:
    1
    2
    3
    4
    inline void redis_cmd_escape(std::string &str)
    {
    replace_all(str, "%", "%%");
    }