CodisLabs / codis

Proxy based Redis cluster solution supporting pipeline and scaling dynamically
MIT License
13.16k stars 2.69k forks source link

对 redis 的 patch 中 slots.c 和 slots_async.c 对 dict 数据结构操作使用的key类型不同 #1318

Open imjustfly opened 7 years ago

imjustfly commented 7 years ago

slots.c 和 slots_async.c 中都涉及到对一些dict数据结构的操作,一些操作比如 dictAdd dictFind 所使用的 key 在 slot.c 中都为 sds,而在 slots_async.c 中都为 robj *

我自己尝试手动合并 codis 的 patch到 redis 4.0.1 版本中,slots_async.c中对dict 的 find 和 add 操作会导致崩溃:

(gdb) thread 5
[Switching to thread 5 (Thread 0x7fbe4f373740 (LWP 34681))]#0  0x00000000004b4676 in siphash (in=0x7fbe46000000 <Address 0x7fbe46000000 out of bounds>,
    inlen=808464432, k=0x768a50 "d04f1ef0cf136f63Linux") at siphash.c:136
136 siphash.c: No such file or directory.
    in siphash.c
(gdb) bt
#0  0x00000000004b4676 in siphash (in=0x7fbe46000000 <Address 0x7fbe46000000 out of bounds>, inlen=808464432, k=0x768a50 "d04f1ef0cf136f63Linux") at siphash.c:136
#1  0x0000000000428d70 in dictGenHashFunction (key=0x7fbe2cac39d8, len=808464432) at dict.c:91
#2  0x000000000042b8aa in dictSdsHash (key=0x7fbe2cac39d8) at server.c:519
#3  0x0000000000429b79 in dictFind (d=0x7fbd994510c0, key=0x7fbe2cac39d8) at dict.c:483
#4  0x00000000004bda4b in batchedObjectIteratorContains (it=0x7fbe48e410f0, key=0x7fbe2cac39d8, usetag=1) at slots_async.c:538
#5  0x00000000004be750 in getSlotsmgrtAsyncClientMigrationStatusOrBlock (c=0x7fbe429f1180, key=0x7fbe2cac39d8, block=0) at slots_async.c:790
#6  0x00000000004bfe2e in slotsmgrtExecWrapperCommand (c=0x7fbe429f1180) at slots_async.c:1284
#7  0x000000000042f6e4 in call (c=0x7fbe429f1180, flags=15) at server.c:2275
#8  0x000000000043026e in processCommand (c=0x7fbe429f1180) at server.c:2557

原因是 调用真正的 hash 函数 siphash 时,如果 key 为 robj * 类型,传递给 siphash 函数的第一个参数 const uint8_t *in 后,内存地址就越界了.

将所有 key 为 robj * 的地方改成使用 sds (sdsdup(robj->ptr) for add, robj->ptr for find & delete) 可以解决这问题。redis 源码对dict使用的key也为sds,是否考虑 slots_async.c 中对key的类型做一下修改,和其他代码统一起来?

spinlock commented 7 years ago

@imjustfly 如果你 patch 成功了。能给我一个 PR 嘛?

这不怪我。Redis 每个版本都是重构,代码数据结构改懂很大 = 重写。这个坑我已经踩过好几次了,从 2.x 到 3.x 再到给 redis 打 patch,每次都不兼容。没有办法,需要你手动 fix,不过好在这部分难度其实不大。几个小时就能搞定。

BTW,到 4.2 的时候,Redis 又改代码了,几乎重构。

spinlock commented 7 years ago

@imjustfly 另外,我觉得是你理解错了。

slots.c 中的 dict 的声明类型是 hashSlotType,而 slots_async.c 里面的 dict 声明类型基本上都是 setDictType。这两种在 redis3.x 分别是用 sdsobj 作为 key。但是在 redis4.x 里面,作者修改了 setDictType 的定义,也改成了 sds 作为 key,就是说,其实是 redis 自己前后不兼容了。

https://github.com/antirez/redis/blob/3.2/src/server.c#L540

/* Sets type hash table */
dictType setDictType = {
    dictEncObjHash,            /* hash function */
    NULL,                      /* key dup */
    NULL,                      /* val dup */
    dictEncObjKeyCompare,      /* key compare */
    dictObjectDestructor, /* key destructor */
    NULL                       /* val destructor */
};

https://github.com/antirez/redis/blob/4.0/src/server.c#L555

/* Set dictionary type. Keys are SDS strings, values are ot used. */
dictType setDictType = {
    dictSdsHash,               /* hash function */
    NULL,                      /* key dup */
    NULL,                      /* val dup */
    dictSdsKeyCompare,         /* key compare */
    dictSdsDestructor,         /* key destructor */
    NULL                       /* val destructor */
};

不是我的原因。

imjustfly commented 7 years ago

@spinlock 收到!我对 redis 代码的历史确实没仔细看。

我这边的patch,redis自带的测试用例都过了,codis slot 相关的命令正在测试,稍等我给你提一下。

我应该会在extern 目录下建一个4.0.1的 redis 目录,里面有一个patch文件提供codis的功能。