summaryrefslogtreecommitdiff
path: root/src/t_zset.c
diff options
context:
space:
mode:
authorOran Agra <oran@redislabs.com>2020-11-02 09:35:37 +0200
committerOran Agra <oran@redislabs.com>2020-12-06 14:54:34 +0200
commit3716950cfc389c0f7ed13fac5bd205173c2d8189 (patch)
tree7e9e73ac7ffda406e85a19c1b365a1a13deab81d /src/t_zset.c
parent5b44631397787a65327fcab77f7df37862286ed9 (diff)
downloadredis-3716950cfc389c0f7ed13fac5bd205173c2d8189.tar.gz
Sanitize dump payload: validate no duplicate records in hash/zset/intset
If RESTORE passes successfully with full sanitization, we can't affort to crash later on assertion due to duplicate records in a hash when converting it form ziplist to dict. This means that when doing full sanitization, we must make sure there are no duplicate records in any of the collections.
Diffstat (limited to 'src/t_zset.c')
-rw-r--r--src/t_zset.c49
1 files changed, 49 insertions, 0 deletions
diff --git a/src/t_zset.c b/src/t_zset.c
index 0b3cc061e..d4ffe1318 100644
--- a/src/t_zset.c
+++ b/src/t_zset.c
@@ -1604,6 +1604,55 @@ robj *zsetDup(robj *o) {
return zobj;
}
+/* callback for to check the ziplist doesn't have duplicate recoreds */
+static int _zsetZiplistValidateIntegrity(unsigned char *p, void *userdata) {
+ struct {
+ long count;
+ dict *fields;
+ } *data = userdata;
+
+ /* Even records are field names, add to dict and check that's not a dup */
+ if (((data->count) & 1) == 1) {
+ unsigned char *str;
+ unsigned int slen;
+ long long vll;
+ if (!ziplistGet(p, &str, &slen, &vll))
+ return 0;
+ sds field = str? sdsnewlen(str, slen): sdsfromlonglong(vll);;
+ if (dictAdd(data->fields, field, NULL) != DICT_OK) {
+ /* Duplicate, return an error */
+ sdsfree(field);
+ return 0;
+ }
+ }
+
+ (data->count)++;
+ return 1;
+}
+
+/* Validate the integrity of the data stracture.
+ * when `deep` is 0, only the integrity of the header is validated.
+ * when `deep` is 1, we scan all the entries one by one. */
+int zsetZiplistValidateIntegrity(unsigned char *zl, size_t size, int deep) {
+ if (!deep)
+ return ziplistValidateIntegrity(zl, size, 0, NULL, NULL);
+
+ /* Keep track of the field names to locate duplicate ones */
+ struct {
+ long count;
+ dict *fields;
+ } data = {0, dictCreate(&hashDictType, NULL)};
+
+ int ret = ziplistValidateIntegrity(zl, size, 1, _zsetZiplistValidateIntegrity, &data);
+
+ /* make sure we have an even number of records. */
+ if (data.count & 1)
+ ret = 0;
+
+ dictRelease(data.fields);
+ return ret;
+}
+
/*-----------------------------------------------------------------------------
* Sorted set commands
*----------------------------------------------------------------------------*/