Fix Integer overflow issue with intsets (CVE-2021-32687)

The vulnerability involves changing the default set-max-intset-entries
configuration parameter to a very large value and constructing specially
crafted commands to manipulate sets
This commit is contained in:
Oran Agra 2021-09-26 15:42:17 +03:00
parent e0cf85b848
commit a30d367a71
3 changed files with 10 additions and 3 deletions

View File

@ -34,6 +34,7 @@
#include "intset.h" #include "intset.h"
#include "zmalloc.h" #include "zmalloc.h"
#include "endianconv.h" #include "endianconv.h"
#include "redisassert.h"
/* Note that these encodings are ordered, so: /* Note that these encodings are ordered, so:
* INTSET_ENC_INT16 < INTSET_ENC_INT32 < INTSET_ENC_INT64. */ * INTSET_ENC_INT16 < INTSET_ENC_INT32 < INTSET_ENC_INT64. */
@ -103,7 +104,8 @@ intset *intsetNew(void) {
/* Resize the intset */ /* Resize the intset */
static intset *intsetResize(intset *is, uint32_t len) { static intset *intsetResize(intset *is, uint32_t len) {
uint32_t size = len*intrev32ifbe(is->encoding); uint64_t size = (uint64_t)len*intrev32ifbe(is->encoding);
assert(size <= SIZE_MAX - sizeof(intset));
is = zrealloc(is,sizeof(intset)+size); is = zrealloc(is,sizeof(intset)+size);
return is; return is;
} }

View File

@ -1518,7 +1518,9 @@ robj *rdbLoadObject(int rdbtype, rio *rdb, sds key) {
if ((len = rdbLoadLen(rdb,NULL)) == RDB_LENERR) return NULL; if ((len = rdbLoadLen(rdb,NULL)) == RDB_LENERR) return NULL;
/* Use a regular set when there are too many entries. */ /* Use a regular set when there are too many entries. */
if (len > server.set_max_intset_entries) { size_t max_entries = server.set_max_intset_entries;
if (max_entries >= 1<<30) max_entries = 1<<30;
if (len > max_entries) {
o = createSetObject(); o = createSetObject();
/* It's faster to expand the dict to the right size asap in order /* It's faster to expand the dict to the right size asap in order
* to avoid rehashing */ * to avoid rehashing */

View File

@ -66,7 +66,10 @@ int setTypeAdd(robj *subject, sds value) {
if (success) { if (success) {
/* Convert to regular set when the intset contains /* Convert to regular set when the intset contains
* too many entries. */ * too many entries. */
if (intsetLen(subject->ptr) > server.set_max_intset_entries) size_t max_entries = server.set_max_intset_entries;
/* limit to 1G entries due to intset internals. */
if (max_entries >= 1<<30) max_entries = 1<<30;
if (intsetLen(subject->ptr) > max_entries)
setTypeConvert(subject,OBJ_ENCODING_HT); setTypeConvert(subject,OBJ_ENCODING_HT);
return 1; return 1;
} }