Compare commits

...
Author SHA1 Message Date
Malavan Sotheeswaran d79298c946 Properly handle case where we delete a key that doesn't exist while a write batch is active 2023-01-20 17:47:33 -08:00
0xflotus c3343ddbd7 fix: small errors in readme (#535) 2023-01-20 16:11:01 -05:00
Malavan SotheeswaranandOran Agra ad0be5666c Security fixes from redis: Avoid integer overflows in SETRANGE and SORT, Fix range issues in ZRANDMEMBER and HRANDFIELD (#547)
* Avoid integer overflows in SETRANGE and SORT (CVE-2022-35977) (#11720)

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.

* Fix range issues in ZRANDMEMBER and HRANDFIELD (CVE-2023-22458) (#11674)

missing range check in ZRANDMEMBER and HRANDIFLD leading to panic due
to protocol limitations

* use std::min/max

* add assert_not_equal

Co-authored-by: Oran Agra <oran@redislabs.com>
2023-01-17 17:10:39 -05:00
Malavan Sotheeswaranandbenschermel b50f0cc21f flash package update (#528)
* ship packages with flash option

* deb12 fix

Co-authored-by: benschermel <bschermel@snapchat.com>
2022-12-29 15:09:53 -05:00
Malavan SotheeswaranandYossi Gottlieb 48e5220d00 TLS test fix from redis (#524)
* Fix TLS tests on newer tcl-tls/OpenSSL. (#10910)

Before this commit, TLS tests on Ubuntu 22.04 would fail as dropped
connections result with an ECONNABORTED error thrown instead of an empty
read.

* multithread for ci tests

* multithread cluster tests

* clients to 1 in ci

Co-authored-by: Yossi Gottlieb <yossigo@gmail.com>
2022-12-29 14:47:06 -05:00
Malavan Sotheeswaran f054ef7872 include bzip zstd lz4 and snappy libs for alpine docker file (#523) 2022-12-15 15:49:54 -05:00
Malavan Sotheeswaran 2498e0fc1f fix macos build warnings/ remove 32 bit CI run (#522)
* fix macos build warnings

* remove 32 bit ci run as we no longer support it
2022-12-15 15:49:44 -05:00
34 changed files with 229 additions and 83 deletions

No files matched your search

+2 -14
View File
@@ -21,10 +21,10 @@ jobs:
- name: test-tls
run: |
sudo apt-get -y install tcl tcl-tls
./runtest --clients 2 --verbose --tls
./runtest --clients 1 --verbose --tls --config server-threads 3
- name: cluster-test
run: |
./runtest-cluster --tls
./runtest-cluster --tls --config server-threads 3
- name: sentinel test
run: |
./runtest-sentinel
@@ -69,15 +69,3 @@ jobs:
sudo apt-get -y remove libzstd || true
sudo apt-get -y install uuid-dev libcurl4-openssl-dev libbz2-dev zlib1g-dev libsnappy-dev liblz4-dev libzstd-dev libgflags-dev
make KEYDB_CFLAGS='-Werror' KEYDB_CXXFLAGS='-Werror' MALLOC=libc -j2
build-ubuntu-32bit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
submodules: recursive
- name: make
run: |
sudo dpkg --add-architecture i386
sudo apt-get update
sudo apt-get -y install gcc-multilib g++-multilib libc6-dev-i386 lib32z1 uuid-dev:i386 libcurl4-openssl-dev:i386
make KEYDB_CFLAGS='-Werror' KEYDB_CXXFLAGS='-Werror' 32bit -j2
+14 -2
View File
@@ -4,7 +4,7 @@
##### KeyDB is now a part of Snap Inc! Check out the announcement [here](https://docs.keydb.dev/news/2022/05/12/keydb-joins-snap)
##### [Release v6.3.0](https://github.com/EQ-Alpha/KeyDB/releases/tag/v6.3.0) is here with major improvements as we consolodate our Open Source and Enterprise offerings into a single BSD-3 licensed project. See our [roadmap](https://docs.keydb.dev/docs/coming-soon) for details.
##### [Release v6.3.0](https://github.com/EQ-Alpha/KeyDB/releases/tag/v6.3.0) is here with major improvements as we consolidate our Open Source and Enterprise offerings into a single BSD-3 licensed project. See our [roadmap](https://docs.keydb.dev/docs/coming-soon) for details.
##### Want to extend KeyDB with Javascript? Try [ModJS](https://github.com/JohnSully/ModJS)
@@ -37,7 +37,7 @@ Because of this difference of opinion features which are right for KeyDB may not
Project Support
-------------------
The KeyDB team maintains this project as part of Snap Inc. KeyDB is used by Snap as part of its caching infrastructure and is fully open sourced. There is no separate commercial product and no paid support options available. We really value collaborating with the open source community and welcome PRs, bug reports, and open discussion. For community support or to get involved further with the project check out our community support options [here](https://docs.keydb.dev/docs/support) (slack, forum, meetup, github issues). Our team monitors these channlels regularly.
The KeyDB team maintains this project as part of Snap Inc. KeyDB is used by Snap as part of its caching infrastructure and is fully open sourced. There is no separate commercial product and no paid support options available. We really value collaborating with the open source community and welcome PRs, bug reports, and open discussion. For community support or to get involved further with the project check out our community support options [here](https://docs.keydb.dev/docs/support) (slack, forum, meetup, github issues). Our team monitors these channels regularly.
Additional Resources
@@ -104,6 +104,12 @@ Avoid forwarding RREPLAY messages to other masters? WARNING: This setting is dan
If you would like KeyDB to dump and load directly to AWS S3 this option specifies the bucket. Using this option with the traditional RDB options will result in KeyDB backing up twice to both locations. If both are specified KeyDB will first attempt to load from the local dump file and if that fails load from S3. This requires the AWS CLI tools to be installed and configured which are used under the hood to transfer the data.
```
storage-provider flash /path/to/flash
```
If you would like to use KeyDB FLASH storage, specify the storage medium followed by the directory path on your local SSD volume. Note that this feature is still considered experimental and should be used with discretion. See [FLASH Documentation](https://docs.keydb.dev/docs/flash) for more details on configuration and setting up your FLASH volume.
Building KeyDB
--------------
@@ -145,6 +151,12 @@ installed):
% ./utils/gen-test-certs.sh
% ./runtest --tls
To build with KeyDB FLASH support, use:
% make ENABLE_FLASH=yes
***Note that the KeyDB FLASH feature is considered experimental (beta) and should used with discretion
Fixing build problems with dependencies or cached build options
---------
+15 -1
View File
@@ -2073,4 +2073,18 @@ replica-weighting-factor 2
#
# By default this is enabled
#
active-client-balancing yes
active-client-balancing yes
# Enable FLASH support (Experimental Feature)
# storage-provider flash /path/to/flash/db
# Blob support is a way to store very large objects (>200MB) on disk
# The files are automatically cleaned up when KeyDB exits and are only
# for temporary use. This helps reduce memory pressure for very large
# data items at the cost of some performance.
#
# By default this config is disable. When enabled the disk associated
# with KeyDB's working directory will be used. If there is insufficient
# disk space or any other I/O error KeyDB will instead use memory.
#
# blob-support false
+14
View File
@@ -2063,3 +2063,17 @@ server-threads 2
#
# By default KeyDB sets this to 2.
replica-weighting-factor 2
# Enable FLASH support (Experimental Feature)
# storage-provider flash /path/to/flash/db
# Blob support is a way to store very large objects (>200MB) on disk
# The files are automatically cleaned up when KeyDB exits and are only
# for temporary use. This helps reduce memory pressure for very large
# data items at the cost of some performance.
#
# By default this config is disable. When enabled the disk associated
# with KeyDB's working directory will be used. If there is insufficient
# disk space or any other I/O error KeyDB will instead use memory.
#
# blob-support false
+5
View File
@@ -19,6 +19,11 @@ elif [ "$distributor" == "Ubuntu" ]; then
fi
codename=$(lsb_release --codename --short)
date=$(date +%a," "%d" "%b" "%Y" "%T)
# overwrite debian bookworm version until updated
if [ $codename == "bookworm" ]; then
distname=+deb12u1
fi
pkg_name=keydb-$majorv:$version$distname
# create build tree
+1
View File
@@ -4,6 +4,7 @@ include /usr/share/dpkg/buildflags.mk
export BUILD_TLS=yes
export USE_SYSTEMD=yes
export ENABLE_FLASH=yes
export CFLAGS CPPFLAGS LDFLAGS
export DEB_BUILD_MAINT_OPTIONS = hardening=+all
export DEB_LDFLAGS_MAINT_APPEND = -ldl -latomic $(LUA_LDFLAGS)
+1
View File
@@ -10,6 +10,7 @@ include /usr/share/dpkg/buildflags.mk
export BUILD_TLS=yes
export USE_SYSTEMD=yes
export ENABLE_FLASH=yes
export CFLAGS CPPFLAGS LDFLAGS
export DEB_BUILD_MAINT_OPTIONS = hardening=+all
export DEB_LDFLAGS_MAINT_APPEND = -ldl -latomic $(LUA_LDFLAGS)
+4
View File
@@ -21,6 +21,10 @@ RUN set -eux; \
bash \
perl \
git \
bzip2-dev \
zstd-dev \
lz4-dev \
snappy-dev \
; \
cd /tmp && git clone --branch $BRANCH https://github.com/Snapchat/KeyDB.git --recursive; \
cd /tmp/KeyDB; \
@@ -2063,3 +2063,17 @@ server-threads 2
#
# By default KeyDB sets this to 2.
replica-weighting-factor 2
# Enable FLASH support (Experimental Feature)
# storage-provider flash /path/to/flash/db
# Blob support is a way to store very large objects (>200MB) on disk
# The files are automatically cleaned up when KeyDB exits and are only
# for temporary use. This helps reduce memory pressure for very large
# data items at the cost of some performance.
#
# By default this config is disable. When enabled the disk associated
# with KeyDB's working directory will be used. If there is insufficient
# disk space or any other I/O error KeyDB will instead use memory.
#
# blob-support false
+2 -2
View File
@@ -2587,7 +2587,7 @@ static int updateMaxclients(long long val, long long prev, const char **err) {
adjustOpenFilesLimit();
if (g_pserver->maxclients != val) {
static char msg[128];
sprintf(msg, "The operating system is not able to handle the specified number of clients, try with %d", g_pserver->maxclients);
snprintf(msg, 128, "The operating system is not able to handle the specified number of clients, try with %d", g_pserver->maxclients);
*err = msg;
if (g_pserver->maxclients > prev) {
g_pserver->maxclients = prev;
@@ -2626,7 +2626,7 @@ static int updateMaxclients(long long val, long long prev, const char **err) {
if (res != AE_OK){
static char msg[128];
sprintf(msg, "Failed to post the request to change setsize for Thread %d", iel);
snprintf(msg, 128,"Failed to post the request to change setsize for Thread %d", iel);
*err = msg;
return 0;
}
+1 -1
View File
@@ -1577,7 +1577,7 @@ char *stringFromLongLong(long long value) {
int len;
char *s;
len = sprintf(buf,"%lld",value);
len = snprintf(buf,32,"%lld",value);
s = zmalloc(len+1);
memcpy(s, buf, len);
s[len] = '\0';
+3 -3
View File
@@ -112,15 +112,15 @@ int endianconvTest(int argc, char *argv[], int accurate) {
UNUSED(argv);
UNUSED(accurate);
sprintf(buf,"ciaoroma");
snprintf(buf,32,"ciaoroma");
memrev16(buf);
printf("%s\n", buf);
sprintf(buf,"ciaoroma");
snprintf(buf,32,"ciaoroma");
memrev32(buf);
printf("%s\n", buf);
sprintf(buf,"ciaoroma");
snprintf(buf,32,"ciaoroma");
memrev64(buf);
printf("%s\n", buf);
+1 -1
View File
@@ -904,7 +904,7 @@ int main(int argc, const char **argv) {
while (self_threads < config.max_threads) {
for (int i = 0; i < config.numclients; i++) {
sprintf(command, "SET %d %s\r\n", self_threads * config.numclients + i, set_value);
snprintf(command, 63, "SET %d %s\r\n", self_threads * config.numclients + i, set_value);
createClient(command, strlen(command), NULL,self_threads);
}
+3 -3
View File
@@ -145,7 +145,7 @@ client *createClient(connection *conn, int iel) {
client_id = g_pserver->next_client_id.fetch_add(1);
c->iel = iel;
c->id = client_id;
sprintf(c->lock.szName, "client %" PRIu64, client_id);
snprintf(c->lock.szName, 56, "client %" PRIu64, client_id);
c->resp = 2;
c->conn = conn;
c->name = NULL;
@@ -777,11 +777,11 @@ void setDeferredAggregateLen(client *c, void *node, long length, char prefix) {
* we return NULL in addReplyDeferredLen() */
if (node == NULL) return;
char lenstr[128];
size_t lenstr_len = sprintf(lenstr, "%c%ld\r\n", prefix, length);
size_t lenstr_len = snprintf(lenstr, 128, "%c%ld\r\n", prefix, length);
setDeferredReply(c, node, lenstr, lenstr_len);
} else {
char lenstr[128];
int lenstr_len = sprintf(lenstr, "%c%ld\r\n", prefix, length);
int lenstr_len = snprintf(lenstr, 128, "%c%ld\r\n", prefix, length);
size_t idxSplice = (size_t)node;
serverAssert(idxSplice <= c->replyAsync->used);
+10 -10
View File
@@ -6835,17 +6835,17 @@ void bytesToHuman(char *s, long long n) {
}
if (n < 1024) {
/* Bytes */
sprintf(s,"%lldB",n);
snprintf(s,256,"%lldB",n);
return;
} else if (n < (1024*1024)) {
d = (double)n/(1024);
sprintf(s,"%.2fK",d);
snprintf(s,256,"%.2fK",d);
} else if (n < (1024LL*1024*1024)) {
d = (double)n/(1024*1024);
sprintf(s,"%.2fM",d);
snprintf(s,256,"%.2fM",d);
} else if (n < (1024LL*1024*1024*1024)) {
d = (double)n/(1024LL*1024*1024);
sprintf(s,"%.2fG",d);
snprintf(s,256,"%.2fG",d);
}
}
@@ -6875,12 +6875,12 @@ static void statMode(void) {
for (j = 0; j < 20; j++) {
long k;
sprintf(buf,"db%d:keys",j);
snprintf(buf,64,"db%d:keys",j);
k = getLongInfoField(reply->str,buf);
if (k == LONG_MIN) continue;
aux += k;
}
sprintf(buf,"%ld",aux);
snprintf(buf,64,"%ld",aux);
printf("%-11s",buf);
/* Used memory */
@@ -6890,23 +6890,23 @@ static void statMode(void) {
/* Clients */
aux = getLongInfoField(reply->str,"connected_clients");
sprintf(buf,"%ld",aux);
snprintf(buf,64,"%ld",aux);
printf(" %-8s",buf);
/* Blocked (BLPOPPING) Clients */
aux = getLongInfoField(reply->str,"blocked_clients");
sprintf(buf,"%ld",aux);
snprintf(buf,64,"%ld",aux);
printf("%-8s",buf);
/* Requests */
aux = getLongInfoField(reply->str,"total_commands_processed");
sprintf(buf,"%ld (+%ld)",aux,requests == 0 ? 0 : aux-requests);
snprintf(buf,64,"%ld (+%ld)",aux,requests == 0 ? 0 : aux-requests);
printf("%-19s",buf);
requests = aux;
/* Connections */
aux = getLongInfoField(reply->str,"total_connections_received");
sprintf(buf,"%ld",aux);
snprintf(buf,64,"%ld",aux);
printf(" %-12s",buf);
/* Children */
+1 -1
View File
@@ -1142,7 +1142,7 @@ public:
}
};
int rdbSaveSnapshotForReplication(struct rdbSaveInfo *rsi) {
int rdbSaveSnapshotForReplication(rdbSaveInfo *rsi) {
// TODO: This needs to be on a background thread
int retval = C_OK;
serverAssert(GlobalLocksAcquired());
+7 -7
View File
@@ -5472,25 +5472,25 @@ void bytesToHuman(char *s, unsigned long long n) {
if (n < 1024) {
/* Bytes */
sprintf(s,"%lluB",n);
snprintf(s,256,"%lluB",n);
} else if (n < (1024*1024)) {
d = (double)n/(1024);
sprintf(s,"%.2fK",d);
snprintf(s,256,"%.2fK",d);
} else if (n < (1024LL*1024*1024)) {
d = (double)n/(1024*1024);
sprintf(s,"%.2fM",d);
snprintf(s,256,"%.2fM",d);
} else if (n < (1024LL*1024*1024*1024)) {
d = (double)n/(1024LL*1024*1024);
sprintf(s,"%.2fG",d);
snprintf(s,256,"%.2fG",d);
} else if (n < (1024LL*1024*1024*1024*1024)) {
d = (double)n/(1024LL*1024*1024*1024);
sprintf(s,"%.2fT",d);
snprintf(s,256,"%.2fT",d);
} else if (n < (1024LL*1024*1024*1024*1024*1024)) {
d = (double)n/(1024LL*1024*1024*1024*1024);
sprintf(s,"%.2fP",d);
snprintf(s,256,"%.2fP",d);
} else {
/* Let's hope we never need this */
sprintf(s,"%lluB",n);
snprintf(s,256,"%lluB",n);
}
}
+6
View File
@@ -1909,6 +1909,12 @@ struct MasterSaveInfo {
masterhost = sdsstring(sdsdup(mi.masterhost));
masterport = mi.masterport;
}
MasterSaveInfo(const MasterSaveInfo &other) {
masterhost = other.masterhost;
masterport = other.masterport;
memcpy(master_replid, other.master_replid, sizeof(master_replid));
master_initial_offset = other.master_initial_offset;
}
MasterSaveInfo &operator=(const MasterSaveInfo &other) {
masterhost = other.masterhost;
+6 -4
View File
@@ -194,8 +194,8 @@ void sortCommand(client *c) {
list *operations;
unsigned int outputlen = 0;
int desc = 0, alpha = 0;
long limit_start = 0, limit_count = -1, start, end;
int j, dontsort = 0, vectorlen;
long limit_start = 0, limit_count = -1, start, end, vectorlen;
int j, dontsort = 0;
int getop = 0; /* GET operation counter */
int int_conversion_error = 0;
int syntax_error = 0;
@@ -321,8 +321,10 @@ void sortCommand(client *c) {
default: vectorlen = 0; serverPanic("Bad SORT type"); /* Avoid GCC warning */
}
/* Perform LIMIT start,count sanity checking. */
start = (limit_start < 0) ? 0 : limit_start;
/* Perform LIMIT start,count sanity checking.
* And avoid integer overflow by limiting inputs to object sizes. */
start = std::min(std::max(limit_start, (long)0), vectorlen);
limit_count = std::min(std::max(limit_count, (long)-1), vectorlen);
end = (limit_count < 0) ? vectorlen-1 : start+limit_count-1;
if (start >= vectorlen) {
start = vectorlen-1;
+6 -4
View File
@@ -100,14 +100,14 @@ bool RocksDBStorageProvider::erase(const char *key, size_t cchKey)
{
rocksdb::Status status;
std::unique_lock<fastlock> l(m_lock);
if (!FKeyExists(key, cchKey))
return false;
if (m_spbatch != nullptr)
{
status = m_spbatch->Delete(m_spcolfamily.get(), rocksdb::Slice(key, cchKey));
}
else
{
if (!FKeyExists(key, cchKey))
return false;
status = m_spdb->Delete(WriteOptions(), m_spcolfamily.get(), rocksdb::Slice(key, cchKey));
}
if (status.ok())
@@ -202,12 +202,12 @@ rocksdb::WriteOptions RocksDBStorageProvider::WriteOptions() const
void RocksDBStorageProvider::beginWriteBatch()
{
m_lock.lock();
m_spbatch = std::make_unique<rocksdb::WriteBatch>();
m_spbatch = std::make_unique<rocksdb::WriteBatchWithIndex>();
}
void RocksDBStorageProvider::endWriteBatch()
{
m_spdb->Write(WriteOptions(), m_spbatch.get());
m_spdb->Write(WriteOptions(), m_spbatch.get()->GetWriteBatch());
m_spbatch = nullptr;
m_lock.unlock();
}
@@ -230,5 +230,7 @@ void RocksDBStorageProvider::flush()
bool RocksDBStorageProvider::FKeyExists(const char *key, size_t cch) const
{
rocksdb::PinnableSlice slice;
if (m_spbatch)
return m_spbatch->GetFromBatchAndDB(m_spdb.get(), ReadOptions(), m_spcolfamily.get(), rocksdb::Slice(key, cch), &slice).ok();
return m_spdb->Get(ReadOptions(), m_spcolfamily.get(), rocksdb::Slice(key, cch), &slice).ok();
}
+2 -1
View File
@@ -3,6 +3,7 @@
#include <memory>
#include "../IStorage.h"
#include <rocksdb/db.h>
#include <rocksdb/utilities/write_batch_with_index.h>
#include "../fastlock.h"
#define INTERNAL_KEY_PREFIX "\x00\x04\x03\x00\x05\x02\x04"
@@ -15,7 +16,7 @@ class RocksDBStorageProvider : public IStorage
{
RocksDBStorageFactory *m_pfactory;
std::shared_ptr<rocksdb::DB> m_spdb; // Note: This must be first so it is deleted last
std::unique_ptr<rocksdb::WriteBatch> m_spbatch;
std::unique_ptr<rocksdb::WriteBatchWithIndex> m_spbatch;
const rocksdb::Snapshot *m_psnapshot = nullptr;
std::shared_ptr<rocksdb::ColumnFamilyHandle> m_spcolfamily;
rocksdb::ReadOptions m_readOptionsTemplate;
+6 -1
View File
@@ -1225,8 +1225,13 @@ void hrandfieldCommand(client *c) {
if (c->argc > 4 || (c->argc == 4 && strcasecmp(szFromObj(c->argv[3]),"withvalues"))) {
addReplyErrorObject(c,shared.syntaxerr);
return;
} else if (c->argc == 4)
} else if (c->argc == 4) {
withvalues = 1;
if (l < LONG_MIN/2 || l > LONG_MAX/2) {
addReplyError(c,"value is out of range");
return;
}
}
hrandfieldWithCountCommand(c, l, withvalues);
return;
}
+1 -1
View File
@@ -263,7 +263,7 @@ sds writeJsonValue(sds output, const char *valIn, size_t cchIn) {
serverAssert(!FSimpleJsonEscapeCh(valIn[ich]));
if (FExtendedJsonEscapeCh(valIn[ich])) {
dst[ichDst++] = '\\'; dst[ichDst++] = 'u';
sprintf(dst + ichDst, "%4x", valIn[ich]);
snprintf(dst + ichDst, cchIn+cchEscapeExtra-ichDst, "%4x", valIn[ich]);
ichDst += 4;
} else {
dst[ichDst++] = valIn[ich];
+11 -6
View File
@@ -38,8 +38,14 @@ int getGenericCommand(client *c);
* String Commands
*----------------------------------------------------------------------------*/
static int checkStringLength(client *c, long long size) {
if (!(c->flags & CLIENT_MASTER) && size > g_pserver->proto_max_bulk_len) {
static int checkStringLength(client *c, long long size, long long append) {
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 > g_pserver->proto_max_bulk_len || total < size || total < append) {
addReplyError(c,"string exceeds maximum allowed size (proto-max-bulk-len)");
return C_ERR;
}
@@ -445,7 +451,7 @@ void setrangeCommand(client *c) {
}
/* 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;
o = createObject(OBJ_STRING,sdsnewlen(NULL, offset+sdslen(value)));
@@ -465,7 +471,7 @@ void setrangeCommand(client *c) {
}
/* 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;
/* Create a copy when the object is shared or encoded. */
@@ -685,8 +691,7 @@ void appendCommand(client *c) {
/* "append" is an argument, so always an sds */
append = c->argv[2];
totlen = stringObjectLen(o)+sdslen((sds)ptrFromObj(append));
if (checkStringLength(c,totlen) != C_OK)
if (checkStringLength(c,stringObjectLen(o),sdslen((sds)ptrFromObj(append))) != C_OK)
return;
/* Append the value */
+6 -1
View File
@@ -4214,8 +4214,13 @@ void zrandmemberCommand(client *c) {
if (c->argc > 4 || (c->argc == 4 && strcasecmp(szFromObj(c->argv[3]),"withscores"))) {
addReplyErrorObject(c,shared.syntaxerr);
return;
} else if (c->argc == 4)
} else if (c->argc == 4) {
withscores = 1;
if (l < LONG_MIN/2 || l > LONG_MAX/2) {
addReplyError(c,"value is out of range");
return;
}
}
zrandmemberWithCountCommand(c, l, withscores);
return;
}
+11 -11
View File
@@ -1704,17 +1704,17 @@ static unsigned char *createIntList() {
unsigned char *zl = ziplistNew();
char buf[32];
sprintf(buf, "100");
snprintf(buf, 32, "100");
zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL);
sprintf(buf, "128000");
snprintf(buf, 32, "128000");
zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL);
sprintf(buf, "-100");
snprintf(buf, 32, "-100");
zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_HEAD);
sprintf(buf, "4294967296");
snprintf(buf, 32, "4294967296");
zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_HEAD);
sprintf(buf, "non integer");
snprintf(buf, 32, "non integer");
zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL);
sprintf(buf, "much much longer non integer");
snprintf(buf, 32, "much much longer non integer");
zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL);
return zl;
}
@@ -2228,7 +2228,7 @@ int ziplistTest(int argc, char **argv, int accurate) {
char buf[32];
int i,len;
for (i = 0; i < 1000; i++) {
len = sprintf(buf,"%d",i);
len = snprintf(buf,32,"%d",i);
zl = ziplistPush(zl,(unsigned char*)buf,len,ZIPLIST_TAIL);
}
for (i = 0; i < 1000; i++) {
@@ -2375,13 +2375,13 @@ int ziplistTest(int argc, char **argv, int accurate) {
} else {
switch(rand() % 3) {
case 0:
buflen = sprintf(buf,"%lld",(0LL + rand()) >> 20);
buflen = snprintf(buf,1024,"%lld",(0LL + rand()) >> 20);
break;
case 1:
buflen = sprintf(buf,"%lld",(0LL + rand()));
buflen = snprintf(buf,1024,"%lld",(0LL + rand()));
break;
case 2:
buflen = sprintf(buf,"%lld",(0LL + rand()) << 20);
buflen = snprintf(buf,1024,"%lld",(0LL + rand()) << 20);
break;
default:
assert(NULL);
@@ -2410,7 +2410,7 @@ int ziplistTest(int argc, char **argv, int accurate) {
assert(ziplistGet(p,&sstr,&slen,&sval));
if (sstr == NULL) {
buflen = sprintf(buf,"%lld",sval);
buflen = snprintf(buf,1024,"%lld",sval);
} else {
buflen = slen;
memcpy(buf,sstr,buflen);
+1 -1
View File
@@ -521,7 +521,7 @@ int jemalloc_purge() {
unsigned narenas = 0;
size_t sz = sizeof(unsigned);
if (!je_mallctl("arenas.narenas", &narenas, &sz, NULL, 0)) {
sprintf(tmp, "arena.%d.purge", narenas);
snprintf(tmp, 32, "arena.%d.purge", narenas);
if (!je_mallctl(tmp, NULL, 0, NULL, 0))
return 0;
}
+36 -5
View File
@@ -66,6 +66,33 @@ proc redis {{server 127.0.0.1} {port 6379} {defer 0} {tls 0} {tlsoptions {}} {re
interp alias {} ::redis::redisHandle$id {} ::redis::__dispatch__ $id
}
# On recent versions of tcl-tls/OpenSSL, reading from a dropped connection
# results with an error we need to catch and mimic the old behavior.
proc ::redis::redis_safe_read {fd len} {
if {$len == -1} {
set err [catch {set val [read $fd]} msg]
} else {
set err [catch {set val [read $fd $len]} msg]
}
if {!$err} {
return $val
}
if {[string match "*connection abort*" $msg]} {
return {}
}
error $msg
}
proc ::redis::redis_safe_gets {fd} {
if {[catch {set val [gets $fd]} msg]} {
if {[string match "*connection abort*" $msg]} {
return {}
}
error $msg
}
return $val
}
# This is a wrapper to the actual dispatching procedure that handles
# reconnection if needed.
proc ::redis::__dispatch__ {id method args} {
@@ -146,6 +173,10 @@ proc ::redis::__method__read {id fd} {
::redis::redis_read_reply $id $fd
}
proc ::redis::__method__rawread {id fd {len -1}} {
return [redis_safe_read $fd $len]
}
proc ::redis::__method__write {id fd buf} {
::redis::redis_write $fd $buf
}
@@ -192,8 +223,8 @@ proc ::redis::redis_writenl {fd buf} {
}
proc ::redis::redis_readnl {fd len} {
set buf [read $fd $len]
read $fd 2 ; # discard CR LF
set buf [redis_safe_read $fd $len]
redis_safe_read $fd 2 ; # discard CR LF
return $buf
}
@@ -239,11 +270,11 @@ proc ::redis::redis_read_map {id fd} {
}
proc ::redis::redis_read_line fd {
string trim [gets $fd]
string trim [redis_safe_gets $fd]
}
proc ::redis::redis_read_null fd {
gets $fd
redis_safe_gets $fd
return {}
}
@@ -260,7 +291,7 @@ proc ::redis::redis_read_reply {id fd} {
}
while {1} {
set type [read $fd 1]
set type [redis_safe_read $fd 1]
switch -exact -- $type {
_ {return [redis_read_null $fd]}
: -
+6
View File
@@ -40,6 +40,12 @@ proc assert_failed {expected_err detail} {
error "assertion:$expected_err $detail"
}
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 ""}} {
if {$expected ne $value} {
assert_failed "Expected '$value' to be equal to '$expected'" $detail
+2 -3
View File
@@ -111,7 +111,7 @@ start_server {tags {"obuf-limits"} overrides { server-threads 1 }} {
# Read nothing
set fd [$rd channel]
assert_equal {} [read $fd]
assert_equal {} [$rd rawread]
}
# Note: This test assumes that what's written with one write, will be read by redis in one read.
@@ -151,8 +151,7 @@ start_server {tags {"obuf-limits"} overrides { server-threads 1 }} {
assert_equal "PONG" [r ping]
set clients [r client list]
assert_no_match "*name=multicommands*" $clients
set fd [$rd2 channel]
assert_equal {} [read $fd]
assert_equal {} [$rd2 rawread]
}
test {Execute transactions completely even if client output buffer limit is enforced} {
+11
View File
@@ -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"
}
}
}
}
+5
View File
@@ -68,6 +68,11 @@ start_server {tags {"hash"}} {
r hrandfield myhash 0
} {}
test "HRANDFIELD count overflow" {
r hmset myhash a 1
assert_error {*value is out of range*} {r hrandfield myhash -9223372036854770000 withvalues}
} {}
test "HRANDFIELD with <count> against non existing key" {
r hrandfield nonexisting_key 100
} {}
+10
View File
@@ -574,4 +574,14 @@ start_server {tags {"string"}} {
test {LCS indexes with match len and minimum match len} {
dict get [r STRALGO LCS IDX KEYS virus1 virus2 WITHMATCHLEN MINMATCHLEN 5] matches
} {{{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"
}
}
}
}
+5
View File
@@ -1714,6 +1714,11 @@ start_server {tags {"zset"}} {
r zrandmember nonexisting_key 100
} {}
test "ZRANDMEMBER count overflow" {
r zadd myzset 0 a
assert_error {*value is out of range*} {r zrandmember myzset -9223372036854770000 withscores}
} {}
# Make sure we can distinguish between an empty array and a null response
r readraw 1