summaryrefslogtreecommitdiff
path: root/src/t_zset.c
diff options
context:
space:
mode:
authorOran Agra <oran@redislabs.com>2021-10-04 12:11:02 +0300
committerGitHub <noreply@github.com>2021-10-04 12:11:02 +0300
commitc5e6a6204c4cf57f85e7c83a9b4e99f1a7204fd2 (patch)
tree9f55ffb0f03b07391b4796331aabcb7881ba80ae /src/t_zset.c
parentfba15850e5c31666e4c3560a3be7fd034fa7e2b6 (diff)
downloadredis-c5e6a6204c4cf57f85e7c83a9b4e99f1a7204fd2.tar.gz
Fix ziplist and listpack overflows and truncations (CVE-2021-32627, CVE-2021-32628) (#9589)
- fix possible heap corruption in ziplist and listpack resulting by trying to allocate more than the maximum size of 4GB. - prevent ziplist (hash and zset) from reaching size of above 1GB, will be converted to HT encoding, that's not a useful size. - prevent listpack (stream) from reaching size of above 1GB. - XADD will start a new listpack if the new record may cause the previous listpack to grow over 1GB. - XADD will respond with an error if a single stream record is over 1GB - List type (ziplist in quicklist) was truncating strings that were over 4GB, now it'll respond with an error. Co-authored-by: sundb <sundbcn@gmail.com>
Diffstat (limited to 'src/t_zset.c')
-rw-r--r--src/t_zset.c62
1 files changed, 39 insertions, 23 deletions
diff --git a/src/t_zset.c b/src/t_zset.c
index a6bfddf46..157ff84c5 100644
--- a/src/t_zset.c
+++ b/src/t_zset.c
@@ -1227,15 +1227,18 @@ void zsetConvert(robj *zobj, int encoding) {
}
/* Convert the sorted set object into a listpack if it is not already a listpack
- * and if the number of elements and the maximum element size is within the
- * expected ranges. */
-void zsetConvertToListpackIfNeeded(robj *zobj, size_t maxelelen) {
+ * and if the number of elements and the maximum element size and total elements size
+ * are within the expected ranges. */
+void zsetConvertToListpackIfNeeded(robj *zobj, size_t maxelelen, size_t totelelen) {
if (zobj->encoding == OBJ_ENCODING_LISTPACK) return;
zset *zset = zobj->ptr;
if (zset->zsl->length <= server.zset_max_listpack_entries &&
- maxelelen <= server.zset_max_listpack_value)
- zsetConvert(zobj,OBJ_ENCODING_LISTPACK);
+ maxelelen <= server.zset_max_listpack_value &&
+ lpSafeToAdd(NULL, totelelen))
+ {
+ zsetConvert(zobj,OBJ_ENCODING_LISTPACK);
+ }
}
/* Return (by reference) the score of the specified member of the sorted set
@@ -1355,20 +1358,28 @@ int zsetAdd(robj *zobj, double score, sds ele, int in_flags, int *out_flags, dou
}
return 1;
} else if (!xx) {
- /* Optimize: check if the element is too large or the list
+ /* check if the element is too large or the list
* becomes too long *before* executing zzlInsert. */
- zobj->ptr = zzlInsert(zobj->ptr,ele,score);
- if (zzlLength(zobj->ptr) > server.zset_max_listpack_entries ||
- sdslen(ele) > server.zset_max_listpack_value)
+ if (zzlLength(zobj->ptr)+1 > server.zset_max_listpack_entries ||
+ sdslen(ele) > server.zset_max_listpack_value ||
+ !lpSafeToAdd(zobj->ptr, sdslen(ele)))
+ {
zsetConvert(zobj,OBJ_ENCODING_SKIPLIST);
- if (newscore) *newscore = score;
- *out_flags |= ZADD_OUT_ADDED;
- return 1;
+ } else {
+ zobj->ptr = zzlInsert(zobj->ptr,ele,score);
+ if (newscore) *newscore = score;
+ *out_flags |= ZADD_OUT_ADDED;
+ return 1;
+ }
} else {
*out_flags |= ZADD_OUT_NOP;
return 1;
}
- } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {
+ }
+
+ /* Note that the above block handling ziplist would have either returned or
+ * converted the key to skiplist. */
+ if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {
zset *zs = zobj->ptr;
zskiplistNode *znode;
dictEntry *de;
@@ -2304,7 +2315,7 @@ inline static void zunionInterAggregate(double *target, double val, int aggregat
}
}
-static int zsetDictGetMaxElementLength(dict *d) {
+static size_t zsetDictGetMaxElementLength(dict *d, size_t *totallen) {
dictIterator *di;
dictEntry *de;
size_t maxelelen = 0;
@@ -2314,6 +2325,8 @@ static int zsetDictGetMaxElementLength(dict *d) {
while((de = dictNext(di)) != NULL) {
sds ele = dictGetKey(de);
if (sdslen(ele) > maxelelen) maxelelen = sdslen(ele);
+ if (totallen)
+ (*totallen) += sdslen(ele);
}
dictReleaseIterator(di);
@@ -2321,7 +2334,7 @@ static int zsetDictGetMaxElementLength(dict *d) {
return maxelelen;
}
-static void zdiffAlgorithm1(zsetopsrc *src, long setnum, zset *dstzset, size_t *maxelelen) {
+static void zdiffAlgorithm1(zsetopsrc *src, long setnum, zset *dstzset, size_t *maxelelen, size_t *totelelen) {
/* DIFF Algorithm 1:
*
* We perform the diff by iterating all the elements of the first set,
@@ -2369,13 +2382,14 @@ static void zdiffAlgorithm1(zsetopsrc *src, long setnum, zset *dstzset, size_t *
znode = zslInsert(dstzset->zsl,zval.score,tmp);
dictAdd(dstzset->dict,tmp,&znode->score);
if (sdslen(tmp) > *maxelelen) *maxelelen = sdslen(tmp);
+ (*totelelen) += sdslen(tmp);
}
}
zuiClearIterator(&src[0]);
}
-static void zdiffAlgorithm2(zsetopsrc *src, long setnum, zset *dstzset, size_t *maxelelen) {
+static void zdiffAlgorithm2(zsetopsrc *src, long setnum, zset *dstzset, size_t *maxelelen, size_t *totelelen) {
/* DIFF Algorithm 2:
*
* Add all the elements of the first set to the auxiliary set.
@@ -2429,7 +2443,7 @@ static void zdiffAlgorithm2(zsetopsrc *src, long setnum, zset *dstzset, size_t *
/* Using this algorithm, we can't calculate the max element as we go,
* we have to iterate through all elements to find the max one after. */
- *maxelelen = zsetDictGetMaxElementLength(dstzset->dict);
+ *maxelelen = zsetDictGetMaxElementLength(dstzset->dict, totelelen);
}
static int zsetChooseDiffAlgorithm(zsetopsrc *src, long setnum) {
@@ -2466,14 +2480,14 @@ static int zsetChooseDiffAlgorithm(zsetopsrc *src, long setnum) {
return (algo_one_work <= algo_two_work) ? 1 : 2;
}
-static void zdiff(zsetopsrc *src, long setnum, zset *dstzset, size_t *maxelelen) {
+static void zdiff(zsetopsrc *src, long setnum, zset *dstzset, size_t *maxelelen, size_t *totelelen) {
/* Skip everything if the smallest input is empty. */
if (zuiLength(&src[0]) > 0) {
int diff_algo = zsetChooseDiffAlgorithm(src, setnum);
if (diff_algo == 1) {
- zdiffAlgorithm1(src, setnum, dstzset, maxelelen);
+ zdiffAlgorithm1(src, setnum, dstzset, maxelelen, totelelen);
} else if (diff_algo == 2) {
- zdiffAlgorithm2(src, setnum, dstzset, maxelelen);
+ zdiffAlgorithm2(src, setnum, dstzset, maxelelen, totelelen);
} else if (diff_algo != 0) {
serverPanic("Unknown algorithm");
}
@@ -2510,7 +2524,7 @@ void zunionInterDiffGenericCommand(client *c, robj *dstkey, int numkeysIndex, in
zsetopsrc *src;
zsetopval zval;
sds tmp;
- size_t maxelelen = 0;
+ size_t maxelelen = 0, totelelen = 0;
robj *dstobj;
zset *dstzset;
zskiplistNode *znode;
@@ -2668,6 +2682,7 @@ void zunionInterDiffGenericCommand(client *c, robj *dstkey, int numkeysIndex, in
tmp = zuiNewSdsFromValue(&zval);
znode = zslInsert(dstzset->zsl,score,tmp);
dictAdd(dstzset->dict,tmp,&znode->score);
+ totelelen += sdslen(tmp);
if (sdslen(tmp) > maxelelen) maxelelen = sdslen(tmp);
}
}
@@ -2704,6 +2719,7 @@ void zunionInterDiffGenericCommand(client *c, robj *dstkey, int numkeysIndex, in
/* Remember the longest single element encountered,
* to understand if it's possible to convert to listpack
* at the end. */
+ totelelen += sdslen(tmp);
if (sdslen(tmp) > maxelelen) maxelelen = sdslen(tmp);
/* Update the element with its initial score. */
dictSetKey(accumulator, de, tmp);
@@ -2738,14 +2754,14 @@ void zunionInterDiffGenericCommand(client *c, robj *dstkey, int numkeysIndex, in
dictReleaseIterator(di);
dictRelease(accumulator);
} else if (op == SET_OP_DIFF) {
- zdiff(src, setnum, dstzset, &maxelelen);
+ zdiff(src, setnum, dstzset, &maxelelen, &totelelen);
} else {
serverPanic("Unknown operator");
}
if (dstkey) {
if (dstzset->zsl->length) {
- zsetConvertToListpackIfNeeded(dstobj, maxelelen);
+ zsetConvertToListpackIfNeeded(dstobj, maxelelen, totelelen);
setKey(c, c->db, dstkey, dstobj);
addReplyLongLong(c, zsetLength(dstobj));
notifyKeyspaceEvent(NOTIFY_ZSET,