Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5a752e1978 | ||
|
|
7916e58211 | ||
|
|
c76d618209 | ||
|
|
f35b72dd17 | ||
|
|
080b99d982 | ||
|
|
d0eeee6e31 | ||
|
|
35eff3d49a | ||
|
|
e7cd611be1 | ||
|
|
89aee9556d | ||
|
|
50e91ca7db |
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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. */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
Reference in New Issue
Block a user