Unverified Commit 93e85347 authored by yoav-steinberg's avatar yoav-steinberg Committed by GitHub
Browse files

Remove argument count limit, dynamically grow argv. (#9528)

Remove hard coded multi-bulk limit (was 1,048,576), new limit is INT_MAX.
When client sends an m-bulk that's higher than 1024, we initially only allocate
the argv array for 1024 arguments, and gradually grow that allocation as arguments
are received.
parent dd3ac97f
...@@ -138,6 +138,7 @@ client *createClient(connection *conn) { ...@@ -138,6 +138,7 @@ client *createClient(connection *conn) {
c->reqtype = 0; c->reqtype = 0;
c->argc = 0; c->argc = 0;
c->argv = NULL; c->argv = NULL;
c->argv_len = 0;
c->argv_len_sum = 0; c->argv_len_sum = 0;
c->original_argc = 0; c->original_argc = 0;
c->original_argv = NULL; c->original_argv = NULL;
...@@ -1209,6 +1210,9 @@ void freeClientArgv(client *c) { ...@@ -1209,6 +1210,9 @@ void freeClientArgv(client *c) {
c->argc = 0; c->argc = 0;
c->cmd = NULL; c->cmd = NULL;
c->argv_len_sum = 0; c->argv_len_sum = 0;
c->argv_len = 0;
zfree(c->argv);
c->argv = NULL;
} }
/* Close all the slaves connections. This is useful in chained replication /* Close all the slaves connections. This is useful in chained replication
...@@ -1446,8 +1450,6 @@ void freeClient(client *c) { ...@@ -1446,8 +1450,6 @@ void freeClient(client *c) {
/* Release other dynamically allocated client structure fields, /* Release other dynamically allocated client structure fields,
* and finally release the client structure itself. */ * and finally release the client structure itself. */
if (c->name) decrRefCount(c->name); if (c->name) decrRefCount(c->name);
zfree(c->argv);
c->argv_len_sum = 0;
freeClientMultiState(c); freeClientMultiState(c);
sdsfree(c->peerid); sdsfree(c->peerid);
sdsfree(c->sockname); sdsfree(c->sockname);
...@@ -1821,7 +1823,8 @@ int processInlineBuffer(client *c) { ...@@ -1821,7 +1823,8 @@ int processInlineBuffer(client *c) {
/* Setup argv array on client structure */ /* Setup argv array on client structure */
if (argc) { if (argc) {
if (c->argv) zfree(c->argv); if (c->argv) zfree(c->argv);
c->argv = zmalloc(sizeof(robj*)*argc); c->argv_len = argc;
c->argv = zmalloc(sizeof(robj*)*c->argv_len);
c->argv_len_sum = 0; c->argv_len_sum = 0;
} }
...@@ -1906,7 +1909,7 @@ int processMultibulkBuffer(client *c) { ...@@ -1906,7 +1909,7 @@ int processMultibulkBuffer(client *c) {
* so go ahead and find out the multi bulk length. */ * so go ahead and find out the multi bulk length. */
serverAssertWithInfo(c,NULL,c->querybuf[c->qb_pos] == '*'); serverAssertWithInfo(c,NULL,c->querybuf[c->qb_pos] == '*');
ok = string2ll(c->querybuf+1+c->qb_pos,newline-(c->querybuf+1+c->qb_pos),&ll); ok = string2ll(c->querybuf+1+c->qb_pos,newline-(c->querybuf+1+c->qb_pos),&ll);
if (!ok || ll > 1024*1024) { if (!ok || ll > INT_MAX) {
addReplyError(c,"Protocol error: invalid multibulk length"); addReplyError(c,"Protocol error: invalid multibulk length");
setProtocolError("invalid mbulk count",c); setProtocolError("invalid mbulk count",c);
return C_ERR; return C_ERR;
...@@ -1920,7 +1923,8 @@ int processMultibulkBuffer(client *c) { ...@@ -1920,7 +1923,8 @@ int processMultibulkBuffer(client *c) {
/* Setup argv array on client structure */ /* Setup argv array on client structure */
if (c->argv) zfree(c->argv); if (c->argv) zfree(c->argv);
c->argv = zmalloc(sizeof(robj*)*c->multibulklen); c->argv_len = min(c->multibulklen, 1024);
c->argv = zmalloc(sizeof(robj*)*c->argv_len);
c->argv_len_sum = 0; c->argv_len_sum = 0;
} }
...@@ -1986,6 +1990,12 @@ int processMultibulkBuffer(client *c) { ...@@ -1986,6 +1990,12 @@ int processMultibulkBuffer(client *c) {
/* Not enough data (+2 == trailing \r\n) */ /* Not enough data (+2 == trailing \r\n) */
break; break;
} else { } else {
/* Check if we have space in argv, grow if needed */
if (c->argc >= c->argv_len) {
c->argv_len = min(c->argv_len < INT_MAX/2 ? c->argv_len*2 : INT_MAX, c->argc+c->multibulklen);
c->argv = zrealloc(c->argv, sizeof(robj*)*c->argv_len);
}
/* Optimization: if the buffer contains JUST our bulk element /* Optimization: if the buffer contains JUST our bulk element
* instead of creating a new object by *copying* the sds we * instead of creating a new object by *copying* the sds we
* just use the current sds string. */ * just use the current sds string. */
...@@ -3186,9 +3196,9 @@ void replaceClientCommandVector(client *c, int argc, robj **argv) { ...@@ -3186,9 +3196,9 @@ void replaceClientCommandVector(client *c, int argc, robj **argv) {
void rewriteClientCommandArgument(client *c, int i, robj *newval) { void rewriteClientCommandArgument(client *c, int i, robj *newval) {
robj *oldval; robj *oldval;
retainOriginalCommandVector(c); retainOriginalCommandVector(c);
if (i >= c->argc) { if (i >= c->argv_len) {
c->argv = zrealloc(c->argv,sizeof(robj*)*(i+1)); c->argv = zrealloc(c->argv,sizeof(robj*)*(i+1));
c->argc = i+1; c->argc = c->argv_len = i+1;
c->argv[i] = NULL; c->argv[i] = NULL;
} }
oldval = c->argv[i]; oldval = c->argv[i];
......
...@@ -939,6 +939,7 @@ typedef struct client { ...@@ -939,6 +939,7 @@ typedef struct client {
size_t querybuf_peak; /* Recent (100ms or more) peak of querybuf size. */ size_t querybuf_peak; /* Recent (100ms or more) peak of querybuf size. */
int argc; /* Num of arguments of current command. */ int argc; /* Num of arguments of current command. */
robj **argv; /* Arguments of current command. */ robj **argv; /* Arguments of current command. */
int argv_len; /* Size of argv array (may be more than argc) */
int original_argc; /* Num of arguments of original command if arguments were rewritten. */ int original_argc; /* Num of arguments of original command if arguments were rewritten. */
robj **original_argv; /* Arguments of original command if arguments were rewritten. */ robj **original_argv; /* Arguments of original command if arguments were rewritten. */
size_t argv_len_sum; /* Sum of lengths of objects in argv list. */ size_t argv_len_sum; /* Sum of lengths of objects in argv list. */
......
...@@ -15,7 +15,7 @@ start_server {tags {"protocol network"}} { ...@@ -15,7 +15,7 @@ start_server {tags {"protocol network"}} {
test "Out of range multibulk length" { test "Out of range multibulk length" {
reconnect reconnect
r write "*20000000\r\n" r write "*3000000000\r\n"
r flush r flush
assert_error "*invalid multibulk length*" {r read} assert_error "*invalid multibulk length*" {r read}
} }
...@@ -196,6 +196,14 @@ start_server {tags {"protocol network"}} { ...@@ -196,6 +196,14 @@ start_server {tags {"protocol network"}} {
r debug protocol verbatim r debug protocol verbatim
} "This is a verbatim\nstring" {needs:debug resp3} } "This is a verbatim\nstring" {needs:debug resp3}
test "test large number of args" {
r flushdb
set args [split [string trim [string repeat "k v " 10000]]]
lappend args "{k}2" v2
r mset {*}$args
assert_equal [r get "{k}2"] v2
}
} }
start_server {tags {"regression"}} { start_server {tags {"regression"}} {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment