Compare commits

...
Author SHA1 Message Date
Malavan Sotheeswaran a354277713 don't do keys or scan async if in transaction 2023-06-01 13:29:18 -07:00
Malavan Sotheeswaran 149cdea0f2 don't do async commands while in eval or exec 2023-05-30 14:08:22 -07:00
Malavan Sotheeswaran 3142b72820 move client_pause_in_transaction to thread local 2023-05-30 11:45:56 -07:00
Malavan Sotheeswaran b7d39c8a42 forgot module.cpp 2023-05-29 22:00:40 -07:00
Malavan Sotheeswaran d795f4fc63 make propogate_in_transaction thread local 2023-05-29 22:00:00 -07:00
Malavan Sotheeswaran 1a60b1854b ignore propagate_in_transaction in processCommand if command is async 2023-05-29 19:18:30 -07:00
Malavan Sotheeswaran ef8b1bf373 add fPreSnapshot to evictionTimeProc 2023-05-25 00:03:39 -07:00
Attila Nagy f629a76a2e Make it compile on FreeBSD (#650) 2023-05-08 15:59:36 -04:00
Malavan Sotheeswaran e4dacb467f add config to disable storage key cache 2023-04-25 11:15:57 -07:00
Malavan Sotheeswaran a8c0f9b1c4 limit number of random elements returned based on config, exit loop early if client is being closed 2023-04-18 17:08:27 -07:00
Malavan Sotheeswaran 36d515ef39 ptrFromObj to szFromObj 2023-04-18 17:08:27 -07:00
Malavan Sotheeswaran 525c97d6cd ->ptr to ptrFromObj 2023-04-18 17:08:27 -07:00
4c200e0985 fix hincrbyfloat not to create a key if the new value is invalid (#11149)
Check the validity of the value before performing the create operation,
prevents new data from being generated even if the request fails to execute.

Co-authored-by: Oran Agra <oran@redislabs.com>
Co-authored-by: chendianqiang <chendianqiang@meituan.com>
Co-authored-by: Binbin <binloveplay1314@qq.com>
2023-04-18 17:08:27 -07:00
Oran Agra d4c0a970e7 Integer Overflow in RAND commands can lead to assertion (CVE-2023-25155) (#11857)
Issue happens when passing a negative long value that greater than
the max positive value that the long can store.
2023-04-18 17:08:27 -07:00
Malavan Sotheeswaran 1735e73a35 don't count base repl buffer size as overhead (#629) 2023-04-12 16:01:22 -04:00
Heng Kuang f9ccf90566 fix the swapdb issue (#620) 2023-04-12 15:08:09 -04:00
17 changed files with 95 additions and 32 deletions

No files matched your search

+9 -1
View File
@@ -145,7 +145,9 @@ FINAL_LDFLAGS=$(LDFLAGS) $(KEYDB_LDFLAGS) $(DEBUG)
FINAL_LIBS+=-lm -lz -lcrypto -lbz2 -lzstd -llz4 -lsnappy
ifneq ($(uname_S),Darwin)
ifneq ($(uname_S),FreeBSD)
FINAL_LIBS+=-latomic
endif
endif
# Linux ARM32 needs -latomic at linking time
ifneq (,$(findstring armv,$(uname_M)))
@@ -211,7 +213,13 @@ ifeq ($(uname_S),NetBSD)
else
ifeq ($(uname_S),FreeBSD)
# FreeBSD
FINAL_LIBS+= -lpthread -lexecinfo
FINAL_LIBS+= -lpthread -luuid -lexecinfo
FINAL_CFLAGS+= -I/usr/local/include
FINAL_CXXFLAGS+= -I/usr/local/include
FINAL_LDFLAGS+= -L/usr/local/lib
ifeq ($(USE_BACKTRACE),yes)
FINAL_CFLAGS+= -DUSE_BACKTRACE
endif
else
ifeq ($(uname_S),DragonFly)
# DragonFly
+1 -1
View File
@@ -21,7 +21,7 @@ dictType dbStorageCacheType = {
StorageCache::StorageCache(IStorage *storage, bool fCache)
: m_spstorage(storage)
{
if (fCache)
if (!g_pserver->flash_disable_key_cache && fCache)
m_pdict = dictCreate(&dbStorageCacheType, nullptr);
}
+2
View File
@@ -2912,6 +2912,7 @@ standardConfig configs[] = {
createLongLongConfig("repl-backlog-size", NULL, MODIFIABLE_CONFIG, 1, LLONG_MAX, g_pserver->repl_backlog_size, 1024*1024, MEMORY_CONFIG, NULL, updateReplBacklogSize), /* Default: 1mb */
createLongLongConfig("repl-backlog-disk-reserve", NULL, IMMUTABLE_CONFIG, 0, LLONG_MAX, cserver.repl_backlog_disk_size, 0, MEMORY_CONFIG, NULL, NULL),
createLongLongConfig("max-snapshot-slip", NULL, MODIFIABLE_CONFIG, 0, 5000, g_pserver->snapshot_slip, 400, 0, NULL, NULL),
createLongLongConfig("max-rand-count", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX/2, g_pserver->rand_total_threshold, LONG_MAX/2, 0, NULL, NULL),
/* Unsigned Long Long configs */
createULongLongConfig("maxmemory", NULL, MODIFIABLE_CONFIG, 0, LLONG_MAX, g_pserver->maxmemory, 0, MEMORY_CONFIG, NULL, updateMaxmemory),
@@ -2939,6 +2940,7 @@ standardConfig configs[] = {
createBoolConfig("allow-write-during-load", NULL, MODIFIABLE_CONFIG, g_pserver->fWriteDuringActiveLoad, 0, NULL, NULL),
createBoolConfig("force-backlog-disk-reserve", NULL, MODIFIABLE_CONFIG, cserver.force_backlog_disk, 0, NULL, NULL),
createBoolConfig("soft-shutdown", NULL, MODIFIABLE_CONFIG, g_pserver->config_soft_shutdown, 0, NULL, NULL),
createBoolConfig("flash-disable-key-cache", NULL, MODIFIABLE_CONFIG, g_pserver->flash_disable_key_cache, 0, NULL, NULL),
createSizeTConfig("semi-ordered-set-bucket-size", NULL, MODIFIABLE_CONFIG, 0, 1024, g_semiOrderedSetTargetBucketSize, 0, INTEGER_CONFIG, NULL, NULL),
#ifdef USE_OPENSSL
+13 -3
View File
@@ -1059,7 +1059,7 @@ void keysCommand(client *c) {
sds pattern = szFromObj(c->argv[1]);
const redisDbPersistentDataSnapshot *snapshot = nullptr;
if (!(c->flags & (CLIENT_MULTI | CLIENT_BLOCKED | CLIENT_DENY_BLOCKING)))
if (!(c->flags & (CLIENT_MULTI | CLIENT_BLOCKED | CLIENT_DENY_BLOCKING)) && !(serverTL->in_eval || serverTL->in_exec))
snapshot = c->db->createSnapshot(c->mvccCheckpoint, true /* fOptional */);
if (snapshot != nullptr)
{
@@ -1224,7 +1224,7 @@ void scanGenericCommand(client *c, robj_roptr o, unsigned long cursor) {
}
}
if (o == nullptr && count >= 100)
if (o == nullptr && count >= 100 && !(serverTL->in_eval || serverTL->in_exec))
{
// Do an async version
if (c->asyncCommand(
@@ -1737,6 +1737,9 @@ int dbSwapDatabases(int id1, int id2) {
id2 < 0 || id2 >= cserver.dbnum) return C_ERR;
if (id1 == id2) return C_OK;
std::swap(g_pserver->db[id1], g_pserver->db[id2]);
//swap db's id too, otherwise db does not match its id
std::swap(g_pserver->db[id1]->id, g_pserver->db[id2]->id);
/* Note that we don't swap blocking_keys,
* ready_keys and watched_keys, since we want clients to
@@ -1766,7 +1769,7 @@ int dbSwapDatabases(int id1, int id2) {
/* SWAPDB db1 db2 */
void swapdbCommand(client *c) {
int id1, id2;
int id1, id2, oriId;
/* Not allowed in cluster mode: we have just DB 0 there. */
if (g_pserver->cluster_enabled) {
@@ -1783,6 +1786,9 @@ void swapdbCommand(client *c) {
"invalid second DB index") != C_OK)
return;
//get client's original db's id
oriId=c->db->id;
/* Swap... */
if (dbSwapDatabases(id1,id2) == C_ERR) {
addReplyError(c,"DB index is out of range");
@@ -1791,6 +1797,10 @@ void swapdbCommand(client *c) {
RedisModuleSwapDbInfo si = {REDISMODULE_SWAPDBINFO_VERSION,(int32_t)id1,(int32_t)id2};
moduleFireServerEvent(REDISMODULE_EVENT_SWAPDB,0,&si);
g_pserver->dirty++;
//set client's db to original db
c->db=g_pserver->db[oriId];
addReply(c,shared.ok);
}
}
+3 -3
View File
@@ -380,7 +380,7 @@ size_t freeMemoryGetNotCountedMemory(void) {
/* also don't count the replication backlog memory
* that's where the replication clients get their memory from */
overhead += g_pserver->repl_backlog_size;
overhead += g_pserver->repl_backlog_size - g_pserver->repl_backlog_config_size;
if (g_pserver->aof_state != AOF_OFF) {
overhead += sdsalloc(g_pserver->aof_buf)+aofRewriteBufferSize();
@@ -552,7 +552,7 @@ static int evictionTimeProc(
UNUSED(clientData);
serverAssert(GlobalLocksAcquired());
if (performEvictions(false) == EVICT_RUNNING) return 0; /* keep evicting */
if (performEvictions((bool)clientData) == EVICT_RUNNING) return 0; /* keep evicting */
/* For EVICT_OK - things are good, no need to keep evicting.
* For EVICT_FAIL - there is nothing left to evict. */
@@ -838,7 +838,7 @@ int performEvictions(bool fPreSnapshot) {
if (!isEvictionProcRunning && serverTL->el != nullptr) {
isEvictionProcRunning = 1;
aeCreateTimeEvent(serverTL->el, 0,
evictionTimeProc, NULL, NULL);
evictionTimeProc, (void*)fPreSnapshot, NULL);
}
break;
}
+2 -2
View File
@@ -656,7 +656,7 @@ void moduleHandlePropagationAfterCommandCallback(RedisModuleCtx *ctx) {
/* We don't need to do anything here if the server isn't inside
* a transaction. */
if (!g_pserver->propagate_in_transaction) return;
if (!serverTL->propagate_in_transaction) return;
/* If this command is executed from with Lua or MULTI/EXEC we do not
* need to propagate EXEC */
@@ -1814,7 +1814,7 @@ void moduleReplicateMultiIfNeeded(RedisModuleCtx *ctx) {
* the module command was called by a script. */
if (serverTL->in_eval || serverTL->in_exec) return;
/* If we already emitted MULTI return ASAP. */
if (g_pserver->propagate_in_transaction) return;
if (serverTL->propagate_in_transaction) return;
/* If this is a thread safe context, we do not want to wrap commands
* executed into MULTI/EXEC, they are executed as single commands
* from an external client in essence. */
+5 -5
View File
@@ -118,14 +118,14 @@ void discardCommand(client *c) {
void beforePropagateMulti() {
/* Propagating MULTI */
serverAssert(!g_pserver->propagate_in_transaction);
g_pserver->propagate_in_transaction = 1;
serverAssert(!serverTL->propagate_in_transaction);
serverTL->propagate_in_transaction = 1;
}
void afterPropagateExec() {
/* Propagating EXEC */
serverAssert(g_pserver->propagate_in_transaction == 1);
g_pserver->propagate_in_transaction = 0;
serverAssert(serverTL->propagate_in_transaction == 1);
serverTL->propagate_in_transaction = 0;
}
/* Send a MULTI command to all the slaves and AOF file. Check the execCommand
@@ -264,7 +264,7 @@ void execCommand(client *c) {
/* Make sure the EXEC command will be propagated as well if MULTI
* was already propagated. */
if (g_pserver->propagate_in_transaction) {
if (serverTL->propagate_in_transaction) {
int is_master = listLength(g_pserver->masters) == 0;
g_pserver->dirty++;
/* If inside the MULTI/EXEC block this instance was suddenly
+2 -2
View File
@@ -2753,7 +2753,7 @@ void readQueryFromClient(connection *conn) {
if (cserver.cthreads > 1 || g_pserver->m_pstorageFactory) {
parseClientCommandBuffer(c);
if (g_pserver->enable_async_commands && !serverTL->disable_async_commands && listLength(g_pserver->monitors) == 0 && (aeLockContention() || serverTL->rgdbSnapshot[c->db->id] || g_fTestMode)) {
if (g_pserver->enable_async_commands && !serverTL->disable_async_commands && listLength(g_pserver->monitors) == 0 && (aeLockContention() || serverTL->rgdbSnapshot[c->db->id] || g_fTestMode) && !serverTL->in_eval && !serverTL->in_exec) {
// Frequent writers aren't good candidates for this optimization, they cause us to renew the snapshot too often
// so we exclude them unless the snapshot we need already exists.
// Note: In test mode we want to create snapshots as often as possibl to excercise them - we don't care about perf
@@ -3995,7 +3995,7 @@ void pauseClients(mstime_t end, pause_type type) {
* to track this state so that we don't assert
* in propagate(). */
if (serverTL->in_exec) {
g_pserver->client_pause_in_transaction = 1;
serverTL->client_pause_in_transaction = 1;
}
}
+7 -8
View File
@@ -3945,8 +3945,6 @@ void initServer(void) {
g_pserver->pubsub_channels = dictCreate(&keylistDictType,NULL);
g_pserver->pubsub_patterns = dictCreate(&keylistDictType,NULL);
g_pserver->cronloops = 0;
g_pserver->propagate_in_transaction = 0;
g_pserver->client_pause_in_transaction = 0;
g_pserver->child_pid = -1;
g_pserver->child_type = CHILD_TYPE_NONE;
g_pserver->rdbThreadVars.fRdbThreadCancel = false;
@@ -4320,12 +4318,12 @@ void propagate(struct redisCommand *cmd, int dbid, robj **argv, int argc,
* This way we'll deliver the MULTI/..../EXEC block as a whole and
* both the AOF and the replication link will have the same consistency
* and atomicity guarantees. */
if (serverTL->in_exec && !g_pserver->propagate_in_transaction)
if (serverTL->in_exec && !serverTL->propagate_in_transaction)
execCommandPropagateMulti(dbid);
/* This needs to be unreachable since the dataset should be fixed during
* client pause, otherwise data may be lossed during a failover. */
serverAssert(!(areClientsPaused() && !g_pserver->client_pause_in_transaction));
serverAssert(!(areClientsPaused() && !serverTL->client_pause_in_transaction));
if (g_pserver->aof_state != AOF_OFF && flags & PROPAGATE_AOF)
feedAppendOnlyFile(cmd,dbid,argv,argc);
@@ -4647,8 +4645,8 @@ void call(client *c, int flags) {
/* Client pause takes effect after a transaction has finished. This needs
* to be located after everything is propagated. */
if (!serverTL->in_exec && g_pserver->client_pause_in_transaction) {
g_pserver->client_pause_in_transaction = 0;
if (!serverTL->in_exec && serverTL->client_pause_in_transaction) {
serverTL->client_pause_in_transaction = 0;
}
/* If the client has keys tracking enabled for client side caching,
@@ -4739,8 +4737,9 @@ int processCommand(client *c, int callFlags) {
/* Both EXEC and EVAL call call() directly so there should be
* no way in_exec or in_eval or propagate_in_transaction is 1.
* That is unless lua_timedout, in which case client may run
* some commands. */
serverAssert(!g_pserver->propagate_in_transaction);
* some commands. Also possible that some other thread set
* propagate_in_transaction if this is an async command. */
serverAssert(!serverTL->propagate_in_transaction);
serverAssert(!serverTL->in_exec);
serverAssert(!serverTL->in_eval);
}
+7 -2
View File
@@ -2197,6 +2197,9 @@ struct redisServerThreadVars {
bool modulesEnabledThisAeLoop = false; /* In this loop of aeMain, were modules enabled before
the thread went to sleep? */
bool disable_async_commands = false; /* this is only valid for one cycle of the AE loop and is reset in afterSleep */
int propagate_in_transaction = 0; /* Make sure we don't propagate nested MULTI/EXEC */
int client_pause_in_transaction = 0; /* Was a client pause executed during this Exec? */
std::vector<client*> vecclientsProcess;
dictAsyncRehashCtl *rehashCtl = nullptr;
@@ -2302,9 +2305,7 @@ struct redisServer {
int sentinel_mode; /* True if this instance is a Sentinel. */
size_t initial_memory_usage; /* Bytes used after initialization. */
int always_show_logo; /* Show logo even for non-stdout logging. */
int propagate_in_transaction; /* Make sure we don't propagate nested MULTI/EXEC */
char *ignore_warnings; /* Config: warnings that should be ignored. */
int client_pause_in_transaction; /* Was a client pause executed during this Exec? */
pause_type client_pause_type; /* True if clients are currently paused */
/* Modules */
::dict *moduleapi; /* Exported core APIs dictionary for modules. */
@@ -2729,9 +2730,13 @@ struct redisServer {
long long repl_batch_offStart = -1;
long long repl_batch_idxStart = -1;
long long rand_total_threshold;
int config_soft_shutdown = false;
bool soft_shutdown = false;
int flash_disable_key_cache = false;
/* Lock Contention Ring Buffer */
static const size_t s_lockContentionSamples = 64;
uint16_t rglockSamples[s_lockContentionSamples];
+10 -2
View File
@@ -729,6 +729,10 @@ void hincrbyfloatCommand(client *c) {
unsigned int vlen;
if (getLongDoubleFromObjectOrReply(c,c->argv[3],&incr,NULL) != C_OK) return;
if (isnan(incr) || isinf(incr)) {
addReplyError(c,"value is NaN or Infinity");
return;
}
if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
if (hashTypeGetValue(o,szFromObj(c->argv[2]),&vstr,&vlen,&ll) == C_OK) {
if (vstr) {
@@ -1062,6 +1066,8 @@ void hrandfieldWithCountCommand(client *c, long l, int withvalues) {
addReplyBulkCBuffer(c, key, sdslen(key));
if (withvalues)
addReplyBulkCBuffer(c, value, sdslen(value));
if (c->flags & CLIENT_CLOSE_ASAP)
break;
}
} else if (hash->encoding == OBJ_ENCODING_ZIPLIST) {
ziplistEntry *keys, *vals = NULL;
@@ -1075,6 +1081,8 @@ void hrandfieldWithCountCommand(client *c, long l, int withvalues) {
count -= sample_count;
ziplistRandomPairs((unsigned char*)ptrFromObj(hash), sample_count, keys, vals);
harndfieldReplyWithZiplist(c, sample_count, keys, vals);
if (c->flags & CLIENT_CLOSE_ASAP)
break;
}
zfree(keys);
zfree(vals);
@@ -1221,13 +1229,13 @@ void hrandfieldCommand(client *c) {
ziplistEntry ele;
if (c->argc >= 3) {
if (getLongFromObjectOrReply(c,c->argv[2],&l,NULL) != C_OK) return;
if (getRangeLongFromObjectOrReply(c,c->argv[2],-LONG_MAX,LONG_MAX,&l,NULL) != C_OK) return;
if (c->argc > 4 || (c->argc == 4 && strcasecmp(szFromObj(c->argv[3]),"withvalues"))) {
addReplyErrorObject(c,shared.syntaxerr);
return;
} else if (c->argc == 4) {
withvalues = 1;
if (l < LONG_MIN/2 || l > LONG_MAX/2) {
if (l < -g_pserver->rand_total_threshold || l > g_pserver->rand_total_threshold) {
addReplyError(c,"value is out of range");
return;
}
+7 -1
View File
@@ -672,7 +672,11 @@ void srandmemberWithCountCommand(client *c) {
dict *d;
if (getLongFromObjectOrReply(c,c->argv[2],&l,NULL) != C_OK) return;
if (getRangeLongFromObjectOrReply(c,c->argv[2],-LONG_MAX,LONG_MAX,&l,NULL) != C_OK) return;
if (l < -g_pserver->rand_total_threshold || l > g_pserver->rand_total_threshold) {
addReplyError(c,"value is out of range");
return;
}
if (l >= 0) {
count = (unsigned long) l;
} else {
@@ -706,6 +710,8 @@ void srandmemberWithCountCommand(client *c) {
} else {
addReplyBulkCBuffer(c,ele,sdslen(ele));
}
if (c->flags & CLIENT_CLOSE_ASAP)
break;
}
return;
}
+6 -2
View File
@@ -4054,6 +4054,8 @@ void zrandmemberWithCountCommand(client *c, long l, int withscores) {
addReplyBulkCBuffer(c, key, sdslen(key));
if (withscores)
addReplyDouble(c, *(double*)dictGetVal(de));
if (c->flags & CLIENT_CLOSE_ASAP)
break;
}
} else if (zsetobj->encoding == OBJ_ENCODING_ZIPLIST) {
ziplistEntry *keys, *vals = NULL;
@@ -4067,6 +4069,8 @@ void zrandmemberWithCountCommand(client *c, long l, int withscores) {
count -= sample_count;
ziplistRandomPairs((unsigned char*)ptrFromObj(zsetobj), sample_count, keys, vals);
zarndmemberReplyWithZiplist(c, sample_count, keys, vals);
if (c->flags & CLIENT_CLOSE_ASAP)
break;
}
zfree(keys);
zfree(vals);
@@ -4210,13 +4214,13 @@ void zrandmemberCommand(client *c) {
ziplistEntry ele;
if (c->argc >= 3) {
if (getLongFromObjectOrReply(c,c->argv[2],&l,NULL) != C_OK) return;
if (getRangeLongFromObjectOrReply(c,c->argv[2],-LONG_MAX,LONG_MAX,&l,NULL) != C_OK) return;
if (c->argc > 4 || (c->argc == 4 && strcasecmp(szFromObj(c->argv[3]),"withscores"))) {
addReplyErrorObject(c,shared.syntaxerr);
return;
} else if (c->argc == 4) {
withscores = 1;
if (l < LONG_MIN/2 || l > LONG_MAX/2) {
if (l < -g_pserver->rand_total_threshold || l > g_pserver->rand_total_threshold) {
addReplyError(c,"value is out of range");
return;
}
+8
View File
@@ -182,4 +182,12 @@ start_server {tags {"obuf-limits"} overrides { server-threads 1 }} {
assert_equal "v2" [r get k2]
assert_equal "v3" [r get k3]
}
test "Obuf limit, HRANDFIELD with huge count stopped mid-run" {
r config set client-output-buffer-limit {normal 1000000 0 0}
r hset myhash a b
catch {r hrandfield myhash -999999999} e
assert_match "*I/O error*" $e
reconnect
}
}
+6
View File
@@ -71,6 +71,8 @@ start_server {tags {"hash"}} {
test "HRANDFIELD count overflow" {
r hmset myhash a 1
assert_error {*value is out of range*} {r hrandfield myhash -9223372036854770000 withvalues}
assert_error {*value is out of range*} {r hrandfield myhash -9223372036854775808 withvalues}
assert_error {*value is out of range*} {r hrandfield myhash -9223372036854775808}
} {}
test "HRANDFIELD with <count> against non existing key" {
@@ -831,4 +833,8 @@ start_server {tags {"hash"}} {
set _ $k
} {ZIP_INT_8B 127 ZIP_INT_16B 32767 ZIP_INT_32B 2147483647 ZIP_INT_64B 9223372036854775808 ZIP_INT_IMM_MIN 0 ZIP_INT_IMM_MAX 12}
test {HINCRBYFLOAT does not allow NaN or Infinity} {
assert_error "*value is NaN or Infinity*" {r hincrbyfloat hfoo field +inf}
assert_equal 0 [r exists hfoo]
}
}
+5
View File
@@ -588,6 +588,11 @@ start_server {
r srandmember nonexisting_key 100
} {}
test "SRANDMEMBER count overflow" {
r sadd myset a
assert_error {*value is out of range*} {r srandmember myset -9223372036854775808}
} {}
# Make sure we can distinguish between an empty array and a null response
r readraw 1
+2
View File
@@ -1717,6 +1717,8 @@ start_server {tags {"zset"}} {
test "ZRANDMEMBER count overflow" {
r zadd myzset 0 a
assert_error {*value is out of range*} {r zrandmember myzset -9223372036854770000 withscores}
assert_error {*value is out of range*} {r zrandmember myzset -9223372036854775808 withscores}
assert_error {*value is out of range*} {r zrandmember myzset -9223372036854775808}
} {}
# Make sure we can distinguish between an empty array and a null response