From 3ba2281f96098691d9d672ce8b484207379b09ee Mon Sep 17 00:00:00 2001 From: sundb <sundbcn@gmail.com> Date: Wed, 2 Dec 2020 03:41:26 +0800 Subject: [PATCH] Improve dbid range check for SELECT, MOVE, COPY (#8085) SELECT used to read the index into a `long` variable, and then pass it to a function that takes an `int`, possibly causing an overflow before the range check. Now all these commands use better and cleaner range check, and that also results in a slight change of the error response in case of an invalid database index. SELECT: in the past it would have returned either `-ERR invalid DB index` (if not a number), or `-ERR DB index is out of range` (if not between 1..16 or alike). now it'll return either `-ERR value is out of range` (if not a number), or `-ERR value is out of range, value must between -2147483648 and 2147483647` (if not in the range for an int), or `-ERR DB index is out of range` (if not between 0..16 or alike) MOVE: in the past it would only fail with `-ERR index out of range` no matter the reason. now return the same errors as the new ones for SELECT mentioned above. (i.e. unlike for SELECT even for a value like 17 we changed the error message) COPY: doesn't really matter how it behaved in the past (new command), new behavior is like the above two. --- src/db.c | 33 ++++++++++++++++----------------- src/object.c | 10 ++++++++++ src/server.h | 1 + tests/unit/keyspace.tcl | 4 ++-- 4 files changed, 29 insertions(+), 19 deletions(-) diff --git a/src/db.c b/src/db.c index e51f79eaa..32baf0974 100644 --- a/src/db.c +++ b/src/db.c @@ -600,10 +600,9 @@ void existsCommand(client *c) { } void selectCommand(client *c) { - long id; + int id; - if (getLongFromObjectOrReply(c, c->argv[1], &id, - "invalid DB index") != C_OK) + if (getIntFromObjectOrReply(c, c->argv[1], &id, NULL) != C_OK) return; if (server.cluster_enabled && id != 0) { @@ -1022,8 +1021,8 @@ void renamenxCommand(client *c) { void moveCommand(client *c) { robj *o; redisDb *src, *dst; - int srcid; - long long dbid, expire; + int srcid, dbid; + long long expire; if (server.cluster_enabled) { addReplyError(c,"MOVE is not allowed in cluster mode"); @@ -1034,11 +1033,11 @@ void moveCommand(client *c) { src = c->db; srcid = c->db->id; - if (getLongLongFromObject(c->argv[2],&dbid) == C_ERR || - dbid < INT_MIN || dbid > INT_MAX || - selectDb(c,dbid) == C_ERR) - { - addReply(c,shared.outofrangeerr); + if (getIntFromObjectOrReply(c, c->argv[2], &dbid, NULL) != C_OK) + return; + + if (selectDb(c,dbid) == C_ERR) { + addReplyError(c,"DB index is out of range"); return; } dst = c->db; @@ -1084,8 +1083,8 @@ void moveCommand(client *c) { void copyCommand(client *c) { robj *o; redisDb *src, *dst; - int srcid; - long long dbid, expire; + int srcid, dbid; + long long expire; int j, replace = 0, delete = 0; /* Obtain source and target DB pointers @@ -1100,11 +1099,11 @@ void copyCommand(client *c) { if (!strcasecmp(c->argv[j]->ptr,"replace")) { replace = 1; } else if (!strcasecmp(c->argv[j]->ptr, "db") && additional >= 1) { - if (getLongLongFromObject(c->argv[j+1], &dbid) == C_ERR || - dbid < INT_MIN || dbid > INT_MAX || - selectDb(c, dbid) == C_ERR) - { - addReplyError(c,"invalid DB index"); + if (getIntFromObjectOrReply(c, c->argv[j+1], &dbid, NULL) != C_OK) + return; + + if (selectDb(c, dbid) == C_ERR) { + addReplyError(c,"DB index is out of range"); return; } dst = c->db; diff --git a/src/object.c b/src/object.c index d35ba0eaf..71eceb6d6 100644 --- a/src/object.c +++ b/src/object.c @@ -747,6 +747,16 @@ int getPositiveLongFromObjectOrReply(client *c, robj *o, long *target, const cha return getRangeLongFromObjectOrReply(c, o, 0, LONG_MAX, target, msg); } +int getIntFromObjectOrReply(client *c, robj *o, int *target, const char *msg) { + long value; + + if (getRangeLongFromObjectOrReply(c, o, INT_MIN, INT_MAX, &value, msg) != C_OK) + return C_ERR; + + *target = value; + return C_OK; +} + char *strEncoding(int encoding) { switch(encoding) { case OBJ_ENCODING_RAW: return "raw"; diff --git a/src/server.h b/src/server.h index a46954096..0bcbeabfb 100644 --- a/src/server.h +++ b/src/server.h @@ -1872,6 +1872,7 @@ int getDoubleFromObject(const robj *o, double *target); int getLongLongFromObject(robj *o, long long *target); int getLongDoubleFromObject(robj *o, long double *target); int getLongDoubleFromObjectOrReply(client *c, robj *o, long double *target, const char *msg); +int getIntFromObjectOrReply(client *c, robj *o, int *target, const char *msg); char *strEncoding(int encoding); int compareStringObjects(robj *a, robj *b); int collateStringObjects(robj *a, robj *b); diff --git a/tests/unit/keyspace.tcl b/tests/unit/keyspace.tcl index a74651db9..c69741814 100644 --- a/tests/unit/keyspace.tcl +++ b/tests/unit/keyspace.tcl @@ -217,7 +217,7 @@ start_server {tags {"keyspace"}} { r set mykey foobar catch {r copy mykey mynewkey DB notanumber} e set e - } {*ERR*invalid DB index} + } {ERR value is not an integer or out of range} test {COPY can copy key expire metadata as well} { r set mykey foobar ex 100 @@ -398,7 +398,7 @@ start_server {tags {"keyspace"}} { r set mykey hello catch {r move mykey notanumber} e set e - } {*ERR*index out of range} + } {ERR value is not an integer or out of range} test {MOVE can move key expire metadata as well} { r select 10