mirror of https://mirror.osredm.com/root/redis.git
Avoid integer overflows in SETRANGE and SORT (CVE-2022-35977)
Authenticated users issuing specially crafted SETRANGE and SORT(_RO) commands can trigger an integer overflow, resulting with Redis attempting to allocate impossible amounts of memory and abort with an OOM panic.
This commit is contained in:
parent
4779ed5ebf
commit
c6bbfec2ff
|
@ -83,6 +83,12 @@ typedef long long ustime_t; /* microsecond time type. */
|
||||||
#include "endianconv.h"
|
#include "endianconv.h"
|
||||||
#include "crc64.h"
|
#include "crc64.h"
|
||||||
|
|
||||||
|
/* min/max */
|
||||||
|
#undef min
|
||||||
|
#undef max
|
||||||
|
#define min(a, b) ((a) < (b) ? (a) : (b))
|
||||||
|
#define max(a, b) ((a) > (b) ? (a) : (b))
|
||||||
|
|
||||||
/* Error codes */
|
/* Error codes */
|
||||||
#define C_OK 0
|
#define C_OK 0
|
||||||
#define C_ERR -1
|
#define C_ERR -1
|
||||||
|
|
|
@ -320,8 +320,10 @@ void sortCommand(client *c) {
|
||||||
default: vectorlen = 0; serverPanic("Bad SORT type"); /* Avoid GCC warning */
|
default: vectorlen = 0; serverPanic("Bad SORT type"); /* Avoid GCC warning */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Perform LIMIT start,count sanity checking. */
|
/* Perform LIMIT start,count sanity checking.
|
||||||
start = (limit_start < 0) ? 0 : limit_start;
|
* And avoid integer overflow by limiting inputs to object sizes. */
|
||||||
|
start = min(max(limit_start, 0), vectorlen);
|
||||||
|
limit_count = min(max(limit_count, -1), vectorlen);
|
||||||
end = (limit_count < 0) ? vectorlen-1 : start+limit_count-1;
|
end = (limit_count < 0) ? vectorlen-1 : start+limit_count-1;
|
||||||
if (start >= vectorlen) {
|
if (start >= vectorlen) {
|
||||||
start = vectorlen-1;
|
start = vectorlen-1;
|
||||||
|
|
|
@ -34,8 +34,14 @@
|
||||||
* String Commands
|
* String Commands
|
||||||
*----------------------------------------------------------------------------*/
|
*----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
static int checkStringLength(client *c, long long size) {
|
static int checkStringLength(client *c, long long size, long long append) {
|
||||||
if (!(c->flags & CLIENT_MASTER) && size > server.proto_max_bulk_len) {
|
if (c->flags & CLIENT_MASTER)
|
||||||
|
return C_OK;
|
||||||
|
/* 'uint64_t' cast is there just to prevent undefined behavior on overflow */
|
||||||
|
long long total = (uint64_t)size + append;
|
||||||
|
/* Test configured max-bulk-len represending a limit of the biggest string object,
|
||||||
|
* and also test for overflow. */
|
||||||
|
if (total > server.proto_max_bulk_len || total < size || total < append) {
|
||||||
addReplyError(c,"string exceeds maximum allowed size (proto-max-bulk-len)");
|
addReplyError(c,"string exceeds maximum allowed size (proto-max-bulk-len)");
|
||||||
return C_ERR;
|
return C_ERR;
|
||||||
}
|
}
|
||||||
|
@ -210,7 +216,7 @@ void setrangeCommand(client *c) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return when the resulting string exceeds allowed size */
|
/* Return when the resulting string exceeds allowed size */
|
||||||
if (checkStringLength(c,offset+sdslen(value)) != C_OK)
|
if (checkStringLength(c,offset,sdslen(value)) != C_OK)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
o = createObject(OBJ_STRING,sdsnewlen(NULL, offset+sdslen(value)));
|
o = createObject(OBJ_STRING,sdsnewlen(NULL, offset+sdslen(value)));
|
||||||
|
@ -230,7 +236,7 @@ void setrangeCommand(client *c) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return when the resulting string exceeds allowed size */
|
/* Return when the resulting string exceeds allowed size */
|
||||||
if (checkStringLength(c,offset+sdslen(value)) != C_OK)
|
if (checkStringLength(c,offset,sdslen(value)) != C_OK)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Create a copy when the object is shared or encoded. */
|
/* Create a copy when the object is shared or encoded. */
|
||||||
|
@ -458,8 +464,7 @@ void appendCommand(client *c) {
|
||||||
|
|
||||||
/* "append" is an argument, so always an sds */
|
/* "append" is an argument, so always an sds */
|
||||||
append = c->argv[2];
|
append = c->argv[2];
|
||||||
totlen = stringObjectLen(o)+sdslen(append->ptr);
|
if (checkStringLength(c,stringObjectLen(o),sdslen(append->ptr)) != C_OK)
|
||||||
if (checkStringLength(c,totlen) != C_OK)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Append the value */
|
/* Append the value */
|
||||||
|
|
|
@ -31,6 +31,12 @@ proc assert_match {pattern value} {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
proc assert_not_equal {value expected {detail ""}} {
|
||||||
|
if {!($expected ne $value)} {
|
||||||
|
assert_failed "Expected '$value' not equal to '$expected'" $detail
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
proc assert_equal {value expected {detail ""}} {
|
proc assert_equal {value expected {detail ""}} {
|
||||||
if {$expected ne $value} {
|
if {$expected ne $value} {
|
||||||
if {$detail ne ""} {
|
if {$detail ne ""} {
|
||||||
|
|
|
@ -315,4 +315,15 @@ start_server {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test {SETRANGE with huge offset} {
|
||||||
|
r lpush L 2 1 0
|
||||||
|
# expecting a different outcome on 32 and 64 bit systems
|
||||||
|
foreach value {9223372036854775807 2147483647} {
|
||||||
|
catch {[r sort_ro L by a limit 2 $value]} res
|
||||||
|
if {![string match "2" $res] && ![string match "*out of range*" $res]} {
|
||||||
|
assert_not_equal $res "expecting an error or 2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -449,4 +449,14 @@ start_server {tags {"string"}} {
|
||||||
test {LCS indexes with match len and minimum match len} {
|
test {LCS indexes with match len and minimum match len} {
|
||||||
dict get [r STRALGO LCS IDX KEYS virus1 virus2 WITHMATCHLEN MINMATCHLEN 5] matches
|
dict get [r STRALGO LCS IDX KEYS virus1 virus2 WITHMATCHLEN MINMATCHLEN 5] matches
|
||||||
} {{{1 222} {13 234} 222}}
|
} {{{1 222} {13 234} 222}}
|
||||||
|
|
||||||
|
test {SETRANGE with huge offset} {
|
||||||
|
foreach value {9223372036854775807 2147483647} {
|
||||||
|
catch {[r setrange K $value A]} res
|
||||||
|
# expecting a different error on 32 and 64 bit systems
|
||||||
|
if {![string match "*string exceeds maximum allowed size*" $res] && ![string match "*out of range*" $res]} {
|
||||||
|
assert_equal $res "expecting an error"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue