Security fixes (#373)

Fixing potential buffer overflows when using the bitop shift and cron commands, as well as comparing ACL user passwords in constant time.

Former-commit-id: 3c716ee33d2d14d517271b674dd57c8328d14522
This commit is contained in:
Vivek Saini 2021-11-09 19:00:01 -05:00 committed by GitHub
parent b3711335c0
commit 2ab0804ee3
3 changed files with 24 additions and 8 deletions

View File

@ -223,12 +223,18 @@ uint64_t ACLGetCommandCategoryFlagByName(const char *name) {
return 0; /* No match. */ return 0; /* No match. */
} }
/* Method for passwords/pattern comparison used for the user->passwords list /* Method for pattern comparison used for the user->patterns list
* so that we can search for items with listSearchKey(). */ * so that we can search for items with listSearchKey(). */
int ACLListMatchSds(void *a, void *b) { int ACLListMatchSds(void *a, void *b) {
return sdscmp((sds)a,(sds)b) == 0; return sdscmp((sds)a,(sds)b) == 0;
} }
/* Method for password comparison used for the user->passwords list
* Like the above, but uses a time independant compare for security reasons */
int ACLListMatchSdsSecure(void *a, void* b) {
return time_independent_strcmp((sds)a,(sds)b) == 0;
}
/* Method to free list elements from ACL users password/patterns lists. */ /* Method to free list elements from ACL users password/patterns lists. */
void ACLListFreeSds(const void *item) { void ACLListFreeSds(const void *item) {
sdsfree((sds)item); sdsfree((sds)item);
@ -253,7 +259,7 @@ user *ACLCreateUser(const char *name, size_t namelen) {
u->passwords = listCreate(); u->passwords = listCreate();
u->patterns = listCreate(); u->patterns = listCreate();
u->channels = listCreate(); u->channels = listCreate();
listSetMatchMethod(u->passwords,ACLListMatchSds); listSetMatchMethod(u->passwords,ACLListMatchSdsSecure);
listSetFreeMethod(u->passwords,ACLListFreeSds); listSetFreeMethod(u->passwords,ACLListFreeSds);
listSetDupMethod(u->passwords,ACLListDupSds); listSetDupMethod(u->passwords,ACLListDupSds);
listSetMatchMethod(u->patterns,ACLListMatchSds); listSetMatchMethod(u->patterns,ACLListMatchSds);

View File

@ -405,6 +405,10 @@ void printBits(unsigned char *p, unsigned long count) {
#define BITFIELDOP_SET 1 #define BITFIELDOP_SET 1
#define BITFIELDOP_INCRBY 2 #define BITFIELDOP_INCRBY 2
/* Make sure we don't bit shift too many bits at a time.
* Even this amount is probably way too large. */
#define BITOP_SHIFT_MAX (1<<30)
/* This helper function used by GETBIT / SETBIT parses the bit offset argument /* This helper function used by GETBIT / SETBIT parses the bit offset argument
* making sure an error is returned if it is negative or if it overflows * making sure an error is returned if it is negative or if it overflows
* Redis 512 MB limit for the string value or more (server.proto_max_bulk_len). * Redis 512 MB limit for the string value or more (server.proto_max_bulk_len).
@ -642,6 +646,11 @@ void bitopCommand(client *c) {
addReplyError(c, "BITOP SHIFT's last parameter must be an integer"); addReplyError(c, "BITOP SHIFT's last parameter must be an integer");
return; return;
} }
if (shift > BITOP_SHIFT_MAX) {
addReplyError(c, "BITOP SHIFT value is too large.");
return;
}
if (op == BITOP_RSHIFT) if (op == BITOP_RSHIFT)
shift = -shift; shift = -shift;
} }
@ -682,7 +691,7 @@ void bitopCommand(client *c) {
if (fShiftOp) if (fShiftOp)
{ {
long newlen = (long)maxlen + shift/CHAR_BIT; long long newlen = (long long)maxlen + shift/CHAR_BIT;
if (shift > 0 && (shift % CHAR_BIT) != 0) if (shift > 0 && (shift % CHAR_BIT) != 0)
newlen++; newlen++;
@ -694,14 +703,14 @@ void bitopCommand(client *c) {
res = (unsigned char*) sdsnewlen(NULL,newlen); res = (unsigned char*) sdsnewlen(NULL,newlen);
if (shift >= 0) if (shift >= 0)
{ // left shift { // left shift
long byteoffset = shift/CHAR_BIT; long long byteoffset = shift/CHAR_BIT;
memset(res, 0, byteoffset); memset(res, 0, byteoffset);
long srcLen = newlen - byteoffset - ((shift % CHAR_BIT) ? 1 : 0); long long srcLen = newlen - byteoffset - ((shift % CHAR_BIT) ? 1 : 0);
// now the bitshift+copy // now the bitshift+copy
unsigned bitshift = shift % CHAR_BIT; unsigned bitshift = shift % CHAR_BIT;
unsigned char carry = 0; unsigned char carry = 0;
for (long iSrc = 0; iSrc < srcLen; ++iSrc) for (long long iSrc = 0; iSrc < srcLen; ++iSrc)
{ {
res[byteoffset+iSrc] = (src[0][iSrc] << bitshift) | carry; res[byteoffset+iSrc] = (src[0][iSrc] << bitshift) | carry;
carry = src[0][iSrc] >> (CHAR_BIT - bitshift); carry = src[0][iSrc] >> (CHAR_BIT - bitshift);
@ -711,14 +720,14 @@ void bitopCommand(client *c) {
} }
else else
{ // right shift { // right shift
long byteoffset = -shift/CHAR_BIT; long long byteoffset = -shift/CHAR_BIT;
unsigned bitshift = -shift % CHAR_BIT; unsigned bitshift = -shift % CHAR_BIT;
if (bitshift) if (bitshift)
++byteoffset; ++byteoffset;
res[0] = (src[0][byteoffset] << (CHAR_BIT-bitshift)); res[0] = (src[0][byteoffset] << (CHAR_BIT-bitshift));
if (byteoffset > 0) if (byteoffset > 0)
res[0] |= (src[0][byteoffset-1] >> bitshift); res[0] |= (src[0][byteoffset-1] >> bitshift);
for (long idx = 1; idx < newlen; ++idx) for (long long idx = 1; idx < newlen; ++idx)
{ {
res[idx] = (src[0][byteoffset+idx] << (CHAR_BIT-bitshift)) | (src[0][byteoffset+idx-1] >> bitshift); res[idx] = (src[0][byteoffset+idx] << (CHAR_BIT-bitshift)) | (src[0][byteoffset+idx-1] >> bitshift);
} }

View File

@ -51,6 +51,7 @@ void cronCommand(client *c)
if (c->argc < (6 + numkeys)) { if (c->argc < (6 + numkeys)) {
addReplyError(c, "Missing arguments or numkeys is too big"); addReplyError(c, "Missing arguments or numkeys is too big");
return;
} }
} }