Compare commits

...
10 Commits
Author SHA1 Message Date
YaacovHazan 5a752e1978 Redis 7.2.10
CI / test-ubuntu-latest (push) Has been cancelled
CI / test-sanitizer-address (push) Has been cancelled
CI / build-debian-old (push) Has been cancelled
CI / build-macos-latest (push) Has been cancelled
CI / build-32bit (push) Has been cancelled
CI / build-libc-malloc (push) Has been cancelled
CI / build-centos-jemalloc (push) Has been cancelled
CI / build-old-chain-jemalloc (push) Has been cancelled
External Server Tests / test-external-standalone (push) Has been cancelled
External Server Tests / test-external-cluster (push) Has been cancelled
External Server Tests / test-external-nodebug (push) Has been cancelled
Spellcheck / Spellcheck (push) Has been cancelled
2025-07-06 15:12:33 +03:00
Mincho Paskalev 7916e58211 Remove string cat usage in tcl tests in order to support tcl8.5 2025-07-06 15:12:33 +03:00
Ozan Tezcan c76d618209 Retry accept() even if accepted connection reports an error (CVE-2025-48367)
In case of accept4() returns an error, we should check errno value and
decide if we should retry accept4() without waiting next event loop iteration.
2025-07-06 15:12:33 +03:00
debing.sunandoranagra f35b72dd17 Fix out of bounds write in hyperloglog commands (CVE-2025-32023)
Co-authored-by: oranagra <oran@redislabs.com>
2025-07-06 15:12:33 +03:00
YaacovHazan 080b99d982 Redis 7.2.9
CI / test-ubuntu-latest (push) Has been cancelled
CI / test-sanitizer-address (push) Has been cancelled
CI / build-debian-old (push) Has been cancelled
CI / build-macos-latest (push) Has been cancelled
CI / build-32bit (push) Has been cancelled
CI / build-libc-malloc (push) Has been cancelled
CI / build-centos-jemalloc (push) Has been cancelled
CI / build-old-chain-jemalloc (push) Has been cancelled
External Server Tests / test-external-standalone (push) Has been cancelled
External Server Tests / test-external-cluster (push) Has been cancelled
External Server Tests / test-external-nodebug (push) Has been cancelled
Spellcheck / Spellcheck (push) Has been cancelled
2025-05-27 15:39:09 +03:00
YaacovHazan d0eeee6e31 Check length of AOF file name in redis-check-aof (CVE-2025-27151)
Ensure that the length of the input file name does not exceed PATH_MAX
2025-05-27 15:39:09 +03:00
debing.sunandmadolson 35eff3d49a Resolve bounds checks on cluster_legacy.c (#13970)
Based on https://github.com/valkey-io/valkey/pull/1463 and
https://github.com/valkey-io/valkey/pull/1481

In the failure of fully
CI(https://github.com/redis/redis/actions/runs/14595343452/job/40979173087?pr=13965)
in version 7.0 we are getting a number of errors like:
```
array subscript ‘clusterMsg[0]’ is partly outside array bounds of ‘unsigned char[2272]’
```

Which is basically GCC telling us that we have an object which is longer
than the underlying storage of the allocation. We actually do this a
lot, but GCC is generally not aware of how big the underlying allocation
is, so it doesn't throw this error. We are specifically getting this
error because the msgBlock can be of variable length depending on the
type of message, but GCC assumes it's the longest one possible. The
solution I went with here was make the message type optional, so that it
wasn't included in the size. I think this also makes some sense, since
it's really just a helper for us to easily cast the object around.

This compilation warning only occurs in version 7.2, because in [this
PR](https://github.com/redis/redis/pull/13073), we started passing
`-flto` to `CFLAGS` by default. It seems that in this case, GCC is
unable to detect such warnings. However, this change is not present in
version 7.2.
So, to reproduce this compilation warning in versions after 7.2, we can
pass `OPTIMIZATION=-O2` manually.

---------

Co-authored-by: madolson <34459052+madolson@users.noreply.github.com>
2025-05-27 15:39:09 +03:00
Vitah Lin e7cd611be1 Fix tls port update not reflected in CLUSTER SLOTS (#13966)
### Problem 

A previous PR (https://github.com/redis/redis/pull/13932) fixed the TCP
port issue in CLUSTER SLOTS, but it seems the handling of the TLS port
was overlooked.

There is this comment in the `addNodeToNodeReply` function in the
`cluster.c` file:
```c
    /* Report TLS ports to TLS client, and report non-TLS port to non-TLS client. */
    addReplyLongLong(c, clusterNodeClientPort(node, shouldReturnTlsInfo()));
    addReplyBulkCBuffer(c, clusterNodeGetName(node), CLUSTER_NAMELEN);
```

### Fixed 

This PR fixes the TLS port issue and adds relevant tests.
2025-05-27 15:39:09 +03:00
nesty92 89aee9556d Fix incorrect lag due to trimming stream via XTRIM or XADD command (#13958)
This PR fix the lag calculation by ensuring that when consumer group's last_id
is behind the first entry, the consumer group's entries read is considered
invalid and recalculated from the start of the stream

Supplement to PR #13473 

Close #13957

Signed-off-by: Ernesto Alejandro Santana Hidalgo <ernesto.alejandrosantana@gmail.com>
2025-05-27 15:39:09 +03:00
Stav-Levi 50e91ca7db Fix port update not reflected in CLUSTER SLOTS (#13932)
Close https://github.com/redis/redis/issues/13892 
config set port cmd updates server.port. cluster slot retrieves
information about cluster slots and their associated nodes. the fix
updates this info when config set port cmd is done, so cluster slots cmd
returns the right value.
2025-05-27 15:39:09 +03:00
16 changed files with 228 additions and 22 deletions
+28
View File
@@ -11,6 +11,34 @@ CRITICAL: There is a critical bug affecting MOST USERS. Upgrade ASAP.
SECURITY: There are security fixes in the release.
--------------------------------------------------------------------------------
================================================================================
Redis 7.2.10 Released Sun 6 Jul 2025 12:00:00 IST
================================================================================
Update urgency: `SECURITY`: There are security fixes in the release.
### Security fixes
* (CVE-2025-32023) Fix out-of-bounds write in `HyperLogLog` commands
* (CVE-2025-48367) Retry accepting other connections even if the accepted connection reports an error
================================================================================
Redis 7.2.9 Released Tue 27 May 2025 12:00:00 IST
================================================================================
Update urgency: `SECURITY`: There are security fixes in the release.
### Security fixes
* (CVE-2025-27151) redis-check-aof may lead to stack overflow and potential RCE
### Bug fixes
- #13966, #13932 `CLUSTER SLOTS` - TLS port update not reflected in CLUSTER SLOTS
- #13958 `XTRIM`, `XADD` - incorrect lag due to trimming stream
================================================================================
Redis 7.2.8 Released Wed 23 Apr 2025 12:00:00 IST
================================================================================
+24
View File
@@ -704,3 +704,27 @@ int anetIsFifo(char *filepath) {
if (stat(filepath, &sb) == -1) return 0;
return S_ISFIFO(sb.st_mode);
}
/* This function must be called after accept4() fails. It returns 1 if 'err'
* indicates accepted connection faced an error, and it's okay to continue
* accepting next connection by calling accept4() again. Other errors either
* indicate programming errors, e.g. calling accept() on a closed fd or indicate
* a resource limit has been reached, e.g. -EMFILE, open fd limit has been
* reached. In the latter case, caller might wait until resources are available.
* See accept4() documentation for details. */
int anetAcceptFailureNeedsRetry(int err) {
if (err == ECONNABORTED)
return 1;
#if defined(__linux__)
/* For details, see 'Error Handling' section on
* https://man7.org/linux/man-pages/man2/accept.2.html */
if (err == ENETDOWN || err == EPROTO || err == ENOPROTOOPT ||
err == EHOSTDOWN || err == ENONET || err == EHOSTUNREACH ||
err == EOPNOTSUPP || err == ENETUNREACH)
{
return 1;
}
#endif
return 0;
}
+1
View File
@@ -71,5 +71,6 @@ int anetPipe(int fds[2], int read_flags, int write_flags);
int anetSetSockMarkId(char *err, int fd, uint32_t id);
int anetGetError(int fd);
int anetIsFifo(char *filepath);
int anetAcceptFailureNeedsRetry(int err);
#endif
+19 -13
View File
@@ -337,9 +337,14 @@ int auxTlsPortPresent(clusterNode *n) {
typedef struct {
size_t totlen; /* Total length of this block including the message */
int refcount; /* Number of cluster link send msg queues containing the message */
clusterMsg msg;
clusterMsg msg[];
} clusterMsgSendBlock;
/* Helper function to extract a normal message from a send block. */
static clusterMsg *getMessageFromSendBlock(clusterMsgSendBlock *msgblock) {
return &msgblock->msg[0];
}
/* -----------------------------------------------------------------------------
* Initialization
* -------------------------------------------------------------------------- */
@@ -1186,12 +1191,12 @@ void clusterReset(int hard) {
* CLUSTER communication link
* -------------------------------------------------------------------------- */
static clusterMsgSendBlock *createClusterMsgSendBlock(int type, uint32_t msglen) {
uint32_t blocklen = msglen + sizeof(clusterMsgSendBlock) - sizeof(clusterMsg);
uint32_t blocklen = msglen + sizeof(clusterMsgSendBlock);
clusterMsgSendBlock *msgblock = zcalloc(blocklen);
msgblock->refcount = 1;
msgblock->totlen = blocklen;
server.stat_cluster_links_memory += blocklen;
clusterBuildMessageHdr(&msgblock->msg,type,msglen);
clusterBuildMessageHdr(getMessageFromSendBlock(msgblock),type,msglen);
return msgblock;
}
@@ -1309,6 +1314,8 @@ void clusterAcceptHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
while(max--) {
cfd = anetTcpAccept(server.neterr, fd, cip, sizeof(cip), &cport);
if (cfd == ANET_ERR) {
if (anetAcceptFailureNeedsRetry(errno))
continue;
if (errno != EWOULDBLOCK)
serverLog(LL_VERBOSE,
"Error accepting cluster node: %s", server.neterr);
@@ -3345,7 +3352,7 @@ void clusterWriteHandler(connection *conn) {
while (totwritten < NET_MAX_WRITES_PER_EVENT && listLength(link->send_msg_queue) > 0) {
listNode *head = listFirst(link->send_msg_queue);
clusterMsgSendBlock *msgblock = (clusterMsgSendBlock*)head->value;
clusterMsg *msg = &msgblock->msg;
clusterMsg *msg = getMessageFromSendBlock(msgblock);
size_t msg_offset = link->head_msg_send_offset;
size_t msg_len = ntohl(msg->totlen);
@@ -3519,7 +3526,7 @@ void clusterSendMessage(clusterLink *link, clusterMsgSendBlock *msgblock) {
if (!link) {
return;
}
if (listLength(link->send_msg_queue) == 0 && msgblock->msg.totlen != 0)
if (listLength(link->send_msg_queue) == 0 && getMessageFromSendBlock(msgblock)->totlen != 0)
connSetWriteHandlerWithBarrier(link->conn, clusterWriteHandler, 1);
listAddNodeTail(link->send_msg_queue, msgblock);
@@ -3530,7 +3537,7 @@ void clusterSendMessage(clusterLink *link, clusterMsgSendBlock *msgblock) {
server.stat_cluster_links_memory += sizeof(listNode);
/* Populate sent messages stats. */
uint16_t type = ntohs(msgblock->msg.type);
uint16_t type = ntohs(getMessageFromSendBlock(msgblock)->type);
if (type < CLUSTERMSG_TYPE_COUNT)
server.cluster->stats_bus_messages_sent[type]++;
}
@@ -3704,7 +3711,7 @@ void clusterSendPing(clusterLink *link, int type) {
* sizeof(clusterMsg) or more. */
if (estlen < (int)sizeof(clusterMsg)) estlen = sizeof(clusterMsg);
clusterMsgSendBlock *msgblock = createClusterMsgSendBlock(type, estlen);
clusterMsg *hdr = &msgblock->msg;
clusterMsg *hdr = getMessageFromSendBlock(msgblock);
if (!link->inbound && type == CLUSTERMSG_TYPE_PING)
link->node->ping_sent = mstime();
@@ -3837,7 +3844,7 @@ clusterMsgSendBlock *clusterCreatePublishMsgBlock(robj *channel, robj *message,
msglen += sizeof(clusterMsgDataPublish) - 8 + channel_len + message_len;
clusterMsgSendBlock *msgblock = createClusterMsgSendBlock(type, msglen);
clusterMsg *hdr = &msgblock->msg;
clusterMsg *hdr = getMessageFromSendBlock(msgblock);
hdr->data.publish.msg.channel_len = htonl(channel_len);
hdr->data.publish.msg.message_len = htonl(message_len);
memcpy(hdr->data.publish.msg.bulk_data,channel->ptr,sdslen(channel->ptr));
@@ -3860,7 +3867,7 @@ void clusterSendFail(char *nodename) {
+ sizeof(clusterMsgDataFail);
clusterMsgSendBlock *msgblock = createClusterMsgSendBlock(CLUSTERMSG_TYPE_FAIL, msglen);
clusterMsg *hdr = &msgblock->msg;
clusterMsg *hdr = getMessageFromSendBlock(msgblock);
memcpy(hdr->data.fail.about.nodename,nodename,CLUSTER_NAMELEN);
clusterBroadcastMessage(msgblock);
@@ -3877,7 +3884,7 @@ void clusterSendUpdate(clusterLink *link, clusterNode *node) {
+ sizeof(clusterMsgDataUpdate);
clusterMsgSendBlock *msgblock = createClusterMsgSendBlock(CLUSTERMSG_TYPE_UPDATE, msglen);
clusterMsg *hdr = &msgblock->msg;
clusterMsg *hdr = getMessageFromSendBlock(msgblock);
memcpy(hdr->data.update.nodecfg.nodename,node->name,CLUSTER_NAMELEN);
hdr->data.update.nodecfg.configEpoch = htonu64(node->configEpoch);
memcpy(hdr->data.update.nodecfg.slots,node->slots,sizeof(node->slots));
@@ -3899,7 +3906,7 @@ void clusterSendModule(clusterLink *link, uint64_t module_id, uint8_t type,
msglen += sizeof(clusterMsgModule) - 3 + len;
clusterMsgSendBlock *msgblock = createClusterMsgSendBlock(CLUSTERMSG_TYPE_MODULE, msglen);
clusterMsg *hdr = &msgblock->msg;
clusterMsg *hdr = getMessageFromSendBlock(msgblock);
hdr->data.module.msg.module_id = module_id; /* Already endian adjusted. */
hdr->data.module.msg.type = type;
hdr->data.module.msg.len = htonl(len);
@@ -3981,11 +3988,10 @@ void clusterRequestFailoverAuth(void) {
uint32_t msglen = sizeof(clusterMsg)-sizeof(union clusterMsgData);
clusterMsgSendBlock *msgblock = createClusterMsgSendBlock(CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST, msglen);
clusterMsg *hdr = &msgblock->msg;
/* If this is a manual failover, set the CLUSTERMSG_FLAG0_FORCEACK bit
* in the header to communicate the nodes receiving the message that
* they should authorized the failover even if the master is working. */
if (server.cluster->mf_end) hdr->mflags[0] |= CLUSTERMSG_FLAG0_FORCEACK;
if (server.cluster->mf_end) msgblock->msg[0].mflags[0] |= CLUSTERMSG_FLAG0_FORCEACK;
clusterBroadcastMessage(msgblock);
clusterMsgSendBlockDecrRefCount(msgblock);
}
+2
View File
@@ -2459,6 +2459,7 @@ static int updatePort(const char **err) {
listener->bindaddr = server.bindaddr;
listener->bindaddr_count = server.bindaddr_count;
listener->port = server.port;
clusterUpdateMyselfAnnouncedPorts();
listener->ct = connectionByType(CONN_TYPE_SOCKET);
if (changeListener(listener) == C_ERR) {
*err = "Unable to listen on this port. Check server logs.";
@@ -2674,6 +2675,7 @@ static int applyTLSPort(const char **err) {
listener->bindaddr_count = server.bindaddr_count;
listener->port = server.tls_port;
listener->ct = connectionByType(CONN_TYPE_TLS);
clusterUpdateMyselfAnnouncedPorts();
if (changeListener(listener) == C_ERR) {
*err = "Unable to listen on this port. Check server logs.";
return 0;
+1 -1
View File
@@ -151,7 +151,7 @@ void evictionPoolPopulate(int dbid, dict *sampledict, dict *keydict, struct evic
for (j = 0; j < count; j++) {
unsigned long long idle;
sds key;
robj *o;
robj *o = NULL;
dictEntry *de;
de = samples[j];
+42 -5
View File
@@ -587,6 +587,7 @@ int hllSparseToDense(robj *o) {
struct hllhdr *hdr, *oldhdr = (struct hllhdr*)sparse;
int idx = 0, runlen, regval;
uint8_t *p = (uint8_t*)sparse, *end = p+sdslen(sparse);
int valid = 1;
/* If the representation is already the right one return ASAP. */
hdr = (struct hllhdr*) sparse;
@@ -606,16 +607,27 @@ int hllSparseToDense(robj *o) {
while(p < end) {
if (HLL_SPARSE_IS_ZERO(p)) {
runlen = HLL_SPARSE_ZERO_LEN(p);
if ((runlen + idx) > HLL_REGISTERS) { /* Overflow. */
valid = 0;
break;
}
idx += runlen;
p++;
} else if (HLL_SPARSE_IS_XZERO(p)) {
runlen = HLL_SPARSE_XZERO_LEN(p);
if ((runlen + idx) > HLL_REGISTERS) { /* Overflow. */
valid = 0;
break;
}
idx += runlen;
p += 2;
} else {
runlen = HLL_SPARSE_VAL_LEN(p);
regval = HLL_SPARSE_VAL_VALUE(p);
if ((runlen + idx) > HLL_REGISTERS) break; /* Overflow. */
if ((runlen + idx) > HLL_REGISTERS) { /* Overflow. */
valid = 0;
break;
}
while(runlen--) {
HLL_DENSE_SET_REGISTER(hdr->registers,idx,regval);
idx++;
@@ -626,7 +638,7 @@ int hllSparseToDense(robj *o) {
/* If the sparse representation was valid, we expect to find idx
* set to HLL_REGISTERS. */
if (idx != HLL_REGISTERS) {
if (!valid || idx != HLL_REGISTERS) {
sdsfree(dense);
return C_ERR;
}
@@ -923,27 +935,40 @@ int hllSparseAdd(robj *o, unsigned char *ele, size_t elesize) {
void hllSparseRegHisto(uint8_t *sparse, int sparselen, int *invalid, int* reghisto) {
int idx = 0, runlen, regval;
uint8_t *end = sparse+sparselen, *p = sparse;
int valid = 1;
while(p < end) {
if (HLL_SPARSE_IS_ZERO(p)) {
runlen = HLL_SPARSE_ZERO_LEN(p);
if ((runlen + idx) > HLL_REGISTERS) { /* Overflow. */
valid = 0;
break;
}
idx += runlen;
reghisto[0] += runlen;
p++;
} else if (HLL_SPARSE_IS_XZERO(p)) {
runlen = HLL_SPARSE_XZERO_LEN(p);
if ((runlen + idx) > HLL_REGISTERS) { /* Overflow. */
valid = 0;
break;
}
idx += runlen;
reghisto[0] += runlen;
p += 2;
} else {
runlen = HLL_SPARSE_VAL_LEN(p);
regval = HLL_SPARSE_VAL_VALUE(p);
if ((runlen + idx) > HLL_REGISTERS) { /* Overflow. */
valid = 0;
break;
}
idx += runlen;
reghisto[regval] += runlen;
p++;
}
}
if (idx != HLL_REGISTERS && invalid) *invalid = 1;
if ((!valid || idx != HLL_REGISTERS) && invalid) *invalid = 1;
}
/* ========================= HyperLogLog Count ==============================
@@ -1091,22 +1116,34 @@ int hllMerge(uint8_t *max, robj *hll) {
} else {
uint8_t *p = hll->ptr, *end = p + sdslen(hll->ptr);
long runlen, regval;
int valid = 1;
p += HLL_HDR_SIZE;
i = 0;
while(p < end) {
if (HLL_SPARSE_IS_ZERO(p)) {
runlen = HLL_SPARSE_ZERO_LEN(p);
if ((runlen + i) > HLL_REGISTERS) { /* Overflow. */
valid = 0;
break;
}
i += runlen;
p++;
} else if (HLL_SPARSE_IS_XZERO(p)) {
runlen = HLL_SPARSE_XZERO_LEN(p);
if ((runlen + i) > HLL_REGISTERS) { /* Overflow. */
valid = 0;
break;
}
i += runlen;
p += 2;
} else {
runlen = HLL_SPARSE_VAL_LEN(p);
regval = HLL_SPARSE_VAL_VALUE(p);
if ((runlen + i) > HLL_REGISTERS) break; /* Overflow. */
if ((runlen + i) > HLL_REGISTERS) { /* Overflow. */
valid = 0;
break;
}
while(runlen--) {
if (regval > max[i]) max[i] = regval;
i++;
@@ -1114,7 +1151,7 @@ int hllMerge(uint8_t *max, robj *hll) {
p++;
}
}
if (i != HLL_REGISTERS) return C_ERR;
if (!valid || i != HLL_REGISTERS) return C_ERR;
}
return C_OK;
}
+6
View File
@@ -547,6 +547,12 @@ int redis_check_aof_main(int argc, char **argv) {
goto invalid_args;
}
/* Check if filepath is longer than PATH_MAX */
if (strlen(filepath) > PATH_MAX) {
printf("Error: filepath is too long (exceeds PATH_MAX)\n");
goto invalid_args;
}
/* In the glibc implementation dirname may modify their argument. */
memcpy(temp_filepath, filepath, strlen(filepath) + 1);
dirpath = dirname(temp_filepath);
+2
View File
@@ -318,6 +318,8 @@ static void connSocketAcceptHandler(aeEventLoop *el, int fd, void *privdata, int
while(max--) {
cfd = anetTcpAccept(server.neterr, fd, cip, sizeof(cip), &cport);
if (cfd == ANET_ERR) {
if (anetAcceptFailureNeedsRetry(errno))
continue;
if (errno != EWOULDBLOCK)
serverLog(LL_WARNING,
"Accepting client connection: %s", server.neterr);
+4 -1
View File
@@ -1711,7 +1711,10 @@ size_t streamReplyWithRange(client *c, stream *s, streamID *start, streamID *end
while(streamIteratorGetID(&si,&id,&numfields)) {
/* Update the group last_id if needed. */
if (group && streamCompareID(&id,&group->last_id) > 0) {
if (group->entries_read != SCG_INVALID_ENTRIES_READ && !streamRangeHasTombstones(s,&group->last_id,NULL)) {
if (group->entries_read != SCG_INVALID_ENTRIES_READ &&
streamCompareID(&group->last_id, &s->first_id) >= 0 &&
!streamRangeHasTombstones(s,&group->last_id,NULL))
{
/* A valid counter and no tombstones between the group's last-delivered-id
* and the stream's last-generated-id mean we can increment the read counter
* to keep tracking the group's progress. */
+2
View File
@@ -774,6 +774,8 @@ static void tlsAcceptHandler(aeEventLoop *el, int fd, void *privdata, int mask)
while(max--) {
cfd = anetTcpAccept(server.neterr, fd, cip, sizeof(cip), &cport);
if (cfd == ANET_ERR) {
if (anetAcceptFailureNeedsRetry(errno))
continue;
if (errno != EWOULDBLOCK)
serverLog(LL_WARNING,
"Accepting client connection: %s", server.neterr);
+2
View File
@@ -100,6 +100,8 @@ static void connUnixAcceptHandler(aeEventLoop *el, int fd, void *privdata, int m
while(max--) {
cfd = anetUnixAccept(server.neterr, fd);
if (cfd == ANET_ERR) {
if (anetAcceptFailureNeedsRetry(errno))
continue;
if (errno != EWOULDBLOCK)
serverLog(LL_WARNING,
"Accepting client connection: %s", server.neterr);
+2 -2
View File
@@ -1,2 +1,2 @@
#define REDIS_VERSION "7.2.8"
#define REDIS_VERSION_NUM 0x00070208
#define REDIS_VERSION "7.2.10"
#define REDIS_VERSION_NUM 0x0007020a
@@ -47,4 +47,29 @@ start_cluster 2 2 {tags {external:skip cluster}} {
R 0 config set cluster-announce-bus-port 0
assert_match "*@$base_bus_port *" [R 0 CLUSTER NODES]
}
test "CONFIG SET port updates cluster-announced port" {
set count [expr [llength $::servers] + 1]
# Get the original port and change to new_port
if {$::tls} {
set orig_port [lindex [R 0 config get tls-port] 1]
} else {
set orig_port [lindex [R 0 config get port] 1]
}
assert {$orig_port != ""}
set new_port [find_available_port $orig_port $count]
if {$::tls} {
R 0 config set tls-port $new_port
} else {
R 0 config set port $new_port
}
# Verify that the new port appears in the output of cluster slots
wait_for_condition 50 100 {
[string match "*$new_port*" [R 0 cluster slots]]
} else {
fail "Cluster announced port was not updated in cluster slots"
}
}
}
+55
View File
@@ -137,6 +137,61 @@ start_server {tags {"hll"}} {
set e
} {*WRONGTYPE*}
test {Corrupted sparse HyperLogLogs doesn't cause overflow and out-of-bounds with XZERO opcode} {
r del hll
# Create a sparse-encoded HyperLogLog header
set header "HYLL"
set payload [binary format c12 {1 0 0 0 0 0 0 0 0 0 0 0}]
set pl [binary format a4a12 $header $payload]
# Create an XZERO opcode with the maximum run length of 16384(2^14)
set runlen [expr 16384 - 1]
set chunk [binary format cc [expr {0b01000000 | ($runlen >> 8)}] [expr {$runlen & 0xff}]]
# Fill the HLL with more than 131072(2^17) XZERO opcodes to make the total
# run length exceed 4GB, will cause an integer overflow.
set repeat [expr 131072 + 1000]
for {set i 0} {$i < $repeat} {incr i} {
append pl $chunk
}
# Create a VAL opcode with a value that will cause out-of-bounds.
append pl [binary format c 0b11111111]
r set hll $pl
# This should not overflow and out-of-bounds.
assert_error {*INVALIDOBJ*} {r pfcount hll hll}
assert_error {*INVALIDOBJ*} {r pfdebug getreg hll}
r ping
}
test {Corrupted sparse HyperLogLogs doesn't cause overflow and out-of-bounds with ZERO opcode} {
r del hll
# Create a sparse-encoded HyperLogLog header
set header "HYLL"
set payload [binary format c12 {1 0 0 0 0 0 0 0 0 0 0 0}]
set pl [binary format a4a12 $header $payload]
# # Create an ZERO opcode with the maximum run length of 64(2^6)
set chunk [binary format c [expr {0b00000000 | 0x3f}]]
# Fill the HLL with more than 33554432(2^17) ZERO opcodes to make the total
# run length exceed 4GB, will cause an integer overflow.
set repeat [expr 33554432 + 1000]
for {set i 0} {$i < $repeat} {incr i} {
append pl $chunk
}
# Create a VAL opcode with a value that will cause out-of-bounds.
append pl [binary format c 0b11111111]
r set hll $pl
# This should not overflow and out-of-bounds.
assert_error {*INVALIDOBJ*} {r pfcount hll hll}
assert_error {*INVALIDOBJ*} {r pfdebug getreg hll}
r ping
}
test {Corrupted dense HyperLogLogs are detected: Wrong length} {
r del hll
r pfadd hll a b c
+13
View File
@@ -1238,6 +1238,19 @@ start_server {
assert_equal [dict get $group entries-read] 1
assert_equal [dict get $group lag] 1
# When all the entries are read, the lag is always 0.
r XREADGROUP GROUP mygroup alice STREAMS x >
set reply [r XINFO STREAM x FULL]
set group [lindex [dict get $reply groups] 0]
assert_equal [dict get $group entries-read] 5
assert_equal [dict get $group lag] 0
r XADD x 6-0 data f
set reply [r XINFO STREAM x FULL]
set group [lindex [dict get $reply groups] 0]
assert_equal [dict get $group entries-read] 5
assert_equal [dict get $group lag] 1
# When all the entries were deleted, the lag is always 0.
r XTRIM x MAXLEN 0
set reply [r XINFO STREAM x FULL]