Skip to content

Commit 4eb23fb

Browse files
author
Holo
committed
Merge tag '7.0.5' into 7.0-cn-annotated
2 parents 387074f + 1571907 commit 4eb23fb

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

66 files changed

+778
-328
lines changed

00-RELEASENOTES

+43
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,49 @@ SECURITY: There are security fixes in the release.
1212
--------------------------------------------------------------------------------
1313

1414

15+
================================================================================
16+
Redis 7.0.5 Released Wed Sep 21 20:00:00 IST 2022
17+
================================================================================
18+
19+
Upgrade urgency: SECURITY, contains fixes to security issues.
20+
21+
Security Fixes:
22+
* (CVE-2022-35951) Executing a XAUTOCLAIM command on a stream key in a specific
23+
state, with a specially crafted COUNT argument, may cause an integer overflow,
24+
a subsequent heap overflow, and potentially lead to remote code execution.
25+
The problem affects Redis versions 7.0.0 or newer
26+
[reported by Xion (SeungHyun Lee) of KAIST GoN].
27+
28+
Module API changes
29+
==================
30+
31+
* Fix RM_Call execution of scripts when used with M/W/S flags to properly
32+
handle script flags (#11159)
33+
* Fix RM_SetAbsExpire and RM_GetAbsExpire API registration (#11025, #8564)
34+
35+
Bug Fixes
36+
=========
37+
38+
* Fix a hang when eviction is combined with lazy-free and maxmemory-eviction-tenacity is set to 100 (#11237)
39+
* Fix a crash when a replica may attempt to set itself as its master as a result of a manual failover (#11263)
40+
* Fix a bug where a cluster-enabled replica node may permanently set its master's hostname to '?' (#10696)
41+
* Fix a crash when a Lua script returns a meta-table (#11032)
42+
43+
Fixes for issues in previous releases of Redis 7.0
44+
--------------------------------------------------
45+
46+
* Fix redis-cli to do DNS lookup before sending CLUSTER MEET (#11151)
47+
* Fix crash when a key is lazy expired during cluster key migration (#11176)
48+
* Fix AOF rewrite to fsync the old AOF file when a new one is created (#11004)
49+
* Fix some crashes involving a list containing entries larger than 1GB (#11242)
50+
* Correctly handle scripts with a non-read-only shebang on a cluster replica (#11223)
51+
* Fix memory leak when unloading a module (#11147)
52+
* Fix bug with scripts ignoring client tracking NOLOOP (#11052)
53+
* Fix client-side tracking breaking protocol when FLUSHDB / FLUSHALL / SWAPDB is used inside MULTI-EXEC (#11038)
54+
* Fix ACL: BITFIELD with GET and also SET / INCRBY can be executed with read-only key permission (#11086)
55+
* Fix missing sections for INFO ALL when also requesting a module info section (#11291)
56+
57+
1558
================================================================================
1659
Redis 7.0.4 Released Monday Jul 18 12:00:00 IST 2022
1760
================================================================================

src/aof.c

+17-2
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ int aofFileExist(char *filename);
5757
int rewriteAppendOnlyFile(char *filename);
5858
aofManifest *aofLoadManifestFromFile(sds am_filepath);
5959
void aofManifestFreeAndUpdate(aofManifest *am);
60+
void aof_background_fsync_and_close(int fd);
6061

6162
/* ----------------------------------------------------------------------------
6263
* AOF Manifest file implementation.
@@ -901,8 +902,14 @@ int openNewIncrAofForAppend(void) {
901902
/* If reaches here, we can safely modify the `server.aof_manifest`
902903
* and `server.aof_fd`. */
903904

904-
/* Close old aof_fd if needed. */
905-
if (server.aof_fd != -1) bioCreateCloseJob(server.aof_fd);
905+
/* fsync and close old aof_fd if needed. In fsync everysec it's ok to delay
906+
* the fsync as long as we grantee it happens, and in fsync always the file
907+
* is already synced at this point so fsync doesn't matter. */
908+
if (server.aof_fd != -1) {
909+
aof_background_fsync_and_close(server.aof_fd);
910+
server.aof_fsync_offset = server.aof_current_size;
911+
server.aof_last_fsync = server.unixtime;
912+
}
906913
server.aof_fd = newfd;
907914

908915
/* Reset the aof_last_incr_size. */
@@ -999,6 +1006,9 @@ int aofRewriteLimited(void) {
9991006
* BIO thread. */
10001007
/* bio 线程是否有 aof fysnc 任务要执行 */
10011008
int aofFsyncInProgress(void) {
1009+
/* Note that we don't care about aof_background_fsync_and_close because
1010+
* server.aof_fd has been replaced by the new INCR AOF file fd,
1011+
* see openNewIncrAofForAppend. */
10021012
return bioPendingJobsOfType(BIO_AOF_FSYNC) != 0;
10031013
}
10041014

@@ -1009,6 +1019,11 @@ void aof_background_fsync(int fd) {
10091019
bioCreateFsyncJob(fd);
10101020
}
10111021

1022+
/* Close the fd on the basis of aof_background_fsync. */
1023+
void aof_background_fsync_and_close(int fd) {
1024+
bioCreateCloseJob(fd, 1);
1025+
}
1026+
10121027
/* Kills an AOFRW child process if exists */
10131028
/* 关闭 aof rewrite 子进程 */
10141029
void killAppendOnlyChild(void) {

src/bio.c

+31-20
Original file line numberDiff line numberDiff line change
@@ -91,15 +91,22 @@ static unsigned long long bio_pending[BIO_NUM_OPS];
9191
/* This structure represents a background Job. It is only used locally to this
9292
* file as the API does not expose the internals at all. */
9393
/* 后台 IO 结构体,仅仅在本地使用,相关的 API 不会被暴露出去 */
94-
struct bio_job {
94+
typedef union bio_job {
9595
/* Job specific arguments.*/
9696
/* 后台任务持有的文件描述符 */
97-
int fd; /* Fd for file based background jobs */
98-
/* 懒释放函数,释放在 free_args 中存储的对象 */
99-
lazy_free_fn *free_fn; /* Function that will free the provided arguments */
100-
/* 要被释放的对象参数 */
101-
void *free_args[]; /* List of arguments to be passed to the free function */
102-
};
97+
struct {
98+
int fd; /* Fd for file based background jobs */
99+
/* 懒释放函数,释放在 free_args 中存储的对象 */
100+
unsigned need_fsync:1; /* A flag to indicate that a fsync is required before
101+
* the file is closed. */
102+
} fd_args;
103+
104+
struct {
105+
lazy_free_fn *free_fn; /* Function that will free the provided arguments */
106+
/* 要被释放的对象参数 */
107+
void *free_args[]; /* List of arguments to be passed to the free function */
108+
} free_args;
109+
} bio_job;
103110

104111
void *bioProcessBackgroundJobs(void *arg);
105112

@@ -150,7 +157,7 @@ void bioInit(void) {
150157
}
151158

152159
/* 提交任务,将任务放入对应类型操作线程的任务列表 */
153-
void bioSubmitJob(int type, struct bio_job *job) {
160+
void bioSubmitJob(int type, bio_job *job) {
154161
pthread_mutex_lock(&bio_mutex[type]);
155162
listAddNodeTail(bio_jobs[type],job);
156163
bio_pending[type]++;
@@ -170,36 +177,37 @@ void bioCreateLazyFreeJob(lazy_free_fn free_fn, int arg_count, ...) {
170177
va_list valist;
171178
/* Allocate memory for the job structure and all required
172179
* arguments */
173-
struct bio_job *job = zmalloc(sizeof(*job) + sizeof(void *) * (arg_count));
174-
job->free_fn = free_fn;
180+
bio_job *job = zmalloc(sizeof(*job) + sizeof(void *) * (arg_count));
181+
job->free_args.free_fn = free_fn;
175182

176183
va_start(valist, arg_count);
177184
for (int i = 0; i < arg_count; i++) {
178-
job->free_args[i] = va_arg(valist, void *);
185+
job->free_args.free_args[i] = va_arg(valist, void *);
179186
}
180187
va_end(valist);
181188
bioSubmitJob(BIO_LAZY_FREE, job);
182189
}
183190

184191
/* 创建 close file 任务,填充 fd 属性,提交任务 */
185-
void bioCreateCloseJob(int fd) {
186-
struct bio_job *job = zmalloc(sizeof(*job));
187-
job->fd = fd;
192+
void bioCreateCloseJob(int fd, int need_fsync) {
193+
bio_job *job = zmalloc(sizeof(*job));
194+
job->fd_args.fd = fd;
195+
job->fd_args.need_fsync = need_fsync;
188196

189197
bioSubmitJob(BIO_CLOSE_FILE, job);
190198
}
191199

192200
/* 创建 AOF file fsync 任务,填充 fd 属性,提交任务 */
193201
void bioCreateFsyncJob(int fd) {
194-
struct bio_job *job = zmalloc(sizeof(*job));
195-
job->fd = fd;
202+
bio_job *job = zmalloc(sizeof(*job));
203+
job->fd_args.fd = fd;
196204

197205
bioSubmitJob(BIO_AOF_FSYNC, job);
198206
}
199207

200208
/* 后台 IO 线程执行任务函数 */
201209
void *bioProcessBackgroundJobs(void *arg) {
202-
struct bio_job *job;
210+
bio_job *job;
203211
unsigned long type = (unsigned long) arg;
204212
sigset_t sigset;
205213

@@ -260,14 +268,17 @@ void *bioProcessBackgroundJobs(void *arg) {
260268
/* Process the job accordingly to its type. */
261269
/* 不同的操作类型执行具体的操作 */
262270
if (type == BIO_CLOSE_FILE) {
271+
if (job->fd_args.need_fsync) {
272+
redis_fsync(job->fd_args.fd);
273+
}
263274
/* 关闭文件 */
264-
close(job->fd);
275+
close(job->fd_args.fd);
265276
} else if (type == BIO_AOF_FSYNC) {
266277
/* The fd may be closed by main thread and reused for another
267278
* socket, pipe, or file. We just ignore these errno because
268279
* aof fsync did not really fail. */
269280
/* 做 fsync,会忽略错误,原子的设置 AOF 的 FSYNC 状态信息 */
270-
if (redis_fsync(job->fd) == -1 &&
281+
if (redis_fsync(job->fd_args.fd) == -1 &&
271282
errno != EBADF && errno != EINVAL)
272283
{
273284
int last_status;
@@ -283,7 +294,7 @@ void *bioProcessBackgroundJobs(void *arg) {
283294
}
284295
} else if (type == BIO_LAZY_FREE) {
285296
/* 调用惰性释放函数释放对象 */
286-
job->free_fn(job->free_args);
297+
job->free_args.free_fn(job->free_args.free_args);
287298
} else {
288299
serverPanic("Wrong job type in bioProcessBackgroundJobs().");
289300
}

src/bio.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ void bioInit(void);
3737
unsigned long long bioPendingJobsOfType(int type);
3838
unsigned long long bioWaitStepOfType(int type);
3939
void bioKillThreads(void);
40-
void bioCreateCloseJob(int fd);
40+
void bioCreateCloseJob(int fd, int need_fsync);
4141
void bioCreateFsyncJob(int fd);
4242
void bioCreateLazyFreeJob(lazy_free_fn free_fn, int arg_count, ...);
4343

src/cluster.c

+32-10
Original file line numberDiff line numberDiff line change
@@ -850,10 +850,15 @@ void setClusterNodeToInboundClusterLink(clusterNode *node, clusterLink *link) {
850850
/* A peer may disconnect and then reconnect with us, and it's not guaranteed that
851851
* we would always process the disconnection of the existing inbound link before
852852
* accepting a new existing inbound link. Therefore, it's possible to have more than
853-
* one inbound link from the same node at the same time. */
853+
* one inbound link from the same node at the same time. Our cleanup logic assumes
854+
* a one to one relationship between nodes and inbound links, so we need to kill
855+
* one of the links. The existing link is more likely the outdated one, but it's
856+
* possible the the other node may need to open another link. */
854857
serverLog(LL_DEBUG, "Replacing inbound link fd %d from node %.40s with fd %d",
855858
node->inbound_link->conn->fd, node->name, link->conn->fd);
859+
freeClusterLink(node->inbound_link);
856860
}
861+
serverAssert(!node->inbound_link);
857862
node->inbound_link = link;
858863
link->node = node;
859864
}
@@ -1810,12 +1815,18 @@ void clusterProcessGossipSection(clusterMsg *hdr, clusterLink *link) {
18101815
/* IP -> string conversion. 'buf' is supposed to at least be 46 bytes.
18111816
* If 'announced_ip' length is non-zero, it is used instead of extracting
18121817
* the IP from the socket peer address. */
1813-
void nodeIp2String(char *buf, clusterLink *link, char *announced_ip) {
1818+
int nodeIp2String(char *buf, clusterLink *link, char *announced_ip) {
18141819
if (announced_ip[0] != '\0') {
18151820
memcpy(buf,announced_ip,NET_IP_STR_LEN);
18161821
buf[NET_IP_STR_LEN-1] = '\0'; /* We are not sure the input is sane. */
1822+
return C_OK;
18171823
} else {
1818-
connPeerToString(link->conn, buf, NET_IP_STR_LEN, NULL);
1824+
if (connPeerToString(link->conn, buf, NET_IP_STR_LEN, NULL) == C_ERR) {
1825+
serverLog(LL_NOTICE, "Error converting peer IP to string: %s",
1826+
link->conn ? connGetLastError(link->conn) : "no link");
1827+
return C_ERR;
1828+
}
1829+
return C_OK;
18191830
}
18201831
}
18211832

@@ -1847,7 +1858,11 @@ int nodeUpdateAddressIfNeeded(clusterNode *node, clusterLink *link,
18471858
* it is safe to call during packet processing. */
18481859
if (link == node->link) return 0;
18491860

1850-
nodeIp2String(ip,link,hdr->myip);
1861+
/* If the peer IP is unavailable for some reasons like invalid fd or closed
1862+
* link, just give up the update this time, and the update will be retried
1863+
* in the next round of PINGs */
1864+
if (nodeIp2String(ip,link,hdr->myip) == C_ERR) return 0;
1865+
18511866
if (node->port == port && node->cport == cport && node->pport == pport &&
18521867
strcmp(ip,node->ip) == 0) return 0;
18531868

@@ -2000,7 +2015,13 @@ void clusterUpdateSlotsConfigWith(clusterNode *sender, uint64_t senderConfigEpoc
20002015
clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG|
20012016
CLUSTER_TODO_UPDATE_STATE|
20022017
CLUSTER_TODO_FSYNC_CONFIG);
2003-
} else if (myself->slaveof && myself->slaveof->slaveof) {
2018+
} else if (myself->slaveof && myself->slaveof->slaveof &&
2019+
/* In some rare case when CLUSTER FAILOVER TAKEOVER is used, it
2020+
* can happen that myself is a replica of a replica of myself. If
2021+
* this happens, we do nothing to avoid a crash and wait for the
2022+
* admin to repair the cluster. */
2023+
myself->slaveof->slaveof != myself)
2024+
{
20042025
/* Safeguard against sub-replicas. A replica's master can turn itself
20052026
* into a replica if its last slot is removed. If no other node takes
20062027
* over the slot, there is nothing else to trigger replica migration. */
@@ -2337,7 +2358,7 @@ int clusterProcessPacket(clusterLink *link) {
23372358
* 传入的 nodename 是 NULL,会随机生成一个节点名来赋值给 node->name */
23382359
node = createClusterNode(NULL,CLUSTER_NODE_HANDSHAKE);
23392360
/* 发送方的端口信息我们是知道了,进行填充,缓冲区中有 */
2340-
nodeIp2String(node->ip,link,hdr->myip);
2361+
serverAssert(nodeIp2String(node->ip,link,hdr->myip) == C_OK);
23412362
node->port = ntohs(hdr->port);
23422363
node->pport = ntohs(hdr->pport);
23432364
node->cport = ntohs(hdr->cport);
@@ -7074,7 +7095,7 @@ clusterNode *getNodeByQuery(client *c, struct redisCommand *cmd, robj **argv, in
70747095
* slot migration, the channel will be served from the source
70757096
* node until the migration completes with CLUSTER SETSLOT <slot>
70767097
* NODE <node-id>. */
7077-
int flags = LOOKUP_NOTOUCH | LOOKUP_NOSTATS | LOOKUP_NONOTIFY;
7098+
int flags = LOOKUP_NOTOUCH | LOOKUP_NOSTATS | LOOKUP_NONOTIFY | LOOKUP_NOEXPIRE;
70787099
if ((migrating_slot || importing_slot) && !is_pubsubshard)
70797100
{
70807101
if (lookupKeyReadWithFlags(&server.db[0], thiskey, flags) == NULL) missing_keys++;
@@ -7088,6 +7109,7 @@ clusterNode *getNodeByQuery(client *c, struct redisCommand *cmd, robj **argv, in
70887109
* without redirections or errors in all the cases. */
70897110
if (n == NULL) return myself;
70907111

7112+
uint64_t cmd_flags = getCommandFlags(c);
70917113
/* Cluster is globally down but we got keys? We only serve the request
70927114
* if it is a read command and when allow_reads_when_down is enabled. */
70937115
if (server.cluster->state != CLUSTER_OK) {
@@ -7101,7 +7123,7 @@ clusterNode *getNodeByQuery(client *c, struct redisCommand *cmd, robj **argv, in
71017123
* cluster is down. */
71027124
if (error_code) *error_code = CLUSTER_REDIR_DOWN_STATE;
71037125
return NULL;
7104-
} else if (cmd->flags & CMD_WRITE) {
7126+
} else if (cmd_flags & CMD_WRITE) {
71057127
/* The cluster is configured to allow read only commands */
71067128
if (error_code) *error_code = CLUSTER_REDIR_DOWN_RO_STATE;
71077129
return NULL;
@@ -7139,7 +7161,7 @@ clusterNode *getNodeByQuery(client *c, struct redisCommand *cmd, robj **argv, in
71397161
* involves multiple keys and we don't have them all, the only option is
71407162
* to send a TRYAGAIN error. */
71417163
if (importing_slot &&
7142-
(c->flags & CLIENT_ASKING || cmd->flags & CMD_ASKING))
7164+
(c->flags & CLIENT_ASKING || cmd_flags & CMD_ASKING))
71437165
{
71447166
if (multiple_keys && missing_keys) {
71457167
if (error_code) *error_code = CLUSTER_REDIR_UNSTABLE;
@@ -7152,7 +7174,7 @@ clusterNode *getNodeByQuery(client *c, struct redisCommand *cmd, robj **argv, in
71527174
/* Handle the read-only client case reading from a slave: if this
71537175
* node is a slave and the request is about a hash slot our master
71547176
* is serving, we can reply without redirection. */
7155-
int is_write_command = (c->cmd->flags & CMD_WRITE) ||
7177+
int is_write_command = (cmd_flags & CMD_WRITE) ||
71567178
(c->cmd->proc == execCommand && (c->mstate.cmd_flags & CMD_WRITE));
71577179
if (((c->flags & CLIENT_READONLY) || is_pubsubshard) &&
71587180
!is_write_command &&

0 commit comments

Comments
 (0)