summaryrefslogtreecommitdiff
path: root/src/networking.c
diff options
context:
space:
mode:
authorBinbin <binloveplay1314@qq.com>2022-01-10 14:21:16 +0800
committerGitHub <noreply@github.com>2022-01-10 08:21:16 +0200
commita1ae260e8addad04e1a73348c8f7bfeab398c2a9 (patch)
tree6ff04fbc9030b4b7870e594c6249b5970827205b /src/networking.c
parentd7479107c742ac41cbe66480fa005c102abba588 (diff)
downloadredis-a1ae260e8addad04e1a73348c8f7bfeab398c2a9.tar.gz
Make sure replicas don't write their own replies to the replication link (#10081)
The following steps will crash redis-server: ``` [root]# cat crash PSYNC replicationid -1 SLOWLOG GET GET key [root]# nc 127.0.0.1 6379 < crash ``` This one following #10020 and the crash was reported in #10076. Other changes about the output info: 1. Cmd with a full name by using `getFullCommandName`, now it will print the right subcommand name like `slowlog|get`. 2. Print the full client info by using `catClientInfoString`, the info is also valuable.:
Diffstat (limited to 'src/networking.c')
-rw-r--r--src/networking.c36
1 files changed, 33 insertions, 3 deletions
diff --git a/src/networking.c b/src/networking.c
index 48f248020..c545e2c3e 100644
--- a/src/networking.c
+++ b/src/networking.c
@@ -362,9 +362,10 @@ void _addReplyToBufferOrList(client *c, const char *s, size_t len) {
* Note this is the simplest way to check a command added a response. Replication links are used to write data but
* not for responses, so we should normally never get here on a replica client. */
if (getClientType(c) == CLIENT_TYPE_SLAVE) {
- serverLog(LL_WARNING, "Replica generated a reply to command %s, disconnecting it",
- c->lastcmd ? c->lastcmd->name : "<unknown>");
- freeClientAsync(c);
+ sds cmdname = c->lastcmd ? getFullCommandName(c->lastcmd) : NULL;
+ logInvalidUseAndFreeClientAsync(c, "Replica generated a reply to command %s",
+ cmdname ? cmdname : "<unknown>");
+ sdsfree(cmdname);
return;
}
@@ -603,6 +604,19 @@ void *addReplyDeferredLen(client *c) {
* ready to be sent, since we are sure that before returning to the
* event loop setDeferredAggregateLen() will be called. */
if (prepareClientToWrite(c) != C_OK) return NULL;
+
+ /* Replicas should normally not cause any writes to the reply buffer. In case a rogue replica sent a command on the
+ * replication link that caused a reply to be generated we'll simply disconnect it.
+ * Note this is the simplest way to check a command added a response. Replication links are used to write data but
+ * not for responses, so we should normally never get here on a replica client. */
+ if (getClientType(c) == CLIENT_TYPE_SLAVE) {
+ sds cmdname = c->lastcmd ? getFullCommandName(c->lastcmd) : NULL;
+ logInvalidUseAndFreeClientAsync(c, "Replica generated a reply to command %s",
+ cmdname ? cmdname : "<unknown>");
+ sdsfree(cmdname);
+ return NULL;
+ }
+
trimReplyUnusedTailSpace(c);
listAddNodeTail(c->reply,NULL); /* NULL is our placeholder. */
return listLast(c->reply);
@@ -1528,6 +1542,22 @@ void freeClientAsync(client *c) {
pthread_mutex_unlock(&async_free_queue_mutex);
}
+/* Log errors for invalid use and free the client in async way.
+ * We will add additional information about the client to the message. */
+void logInvalidUseAndFreeClientAsync(client *c, const char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ sds info = sdscatvprintf(sdsempty(), fmt, ap);
+ va_end(ap);
+
+ sds client = catClientInfoString(sdsempty(), c);
+ serverLog(LL_WARNING, "%s, disconnecting it: %s", info, client);
+
+ sdsfree(info);
+ sdsfree(client);
+ freeClientAsync(c);
+}
+
/* Perform processing of the client before moving on to processing the next client
* this is useful for performing operations that affect the global state but can't
* wait until we're done with all clients. In other words can't wait until beforeSleep()