From 2ff3fc1790142332d6ae827db799208b4da03df3 Mon Sep 17 00:00:00 2001 From: yoav-steinberg Date: Sun, 2 Jan 2022 11:18:43 +0200 Subject: [PATCH] Make sure replicas don't write their own replies to the replication link (#10020) Since #9166 we have an assertion here to make sure replica clients don't write anything to their buffer. But in reality a replica may attempt write data to it's buffer simply by sending a command on the replication link. This command in most cases will be rejected since #8868 but it'll still generate an error. Actually the only valid command to send on a replication link is 'REPCONF ACK` which generates no response. We want to keep the design so that replicas can send commands but we need to avoid any situation where we start putting data in their response buffers, especially since they aren't used anymore. This PR makes sure to disconnect a rogue client which generated a write on the replication link that cause something to be written to the response buffer. To recreate the bug this fixes simply connect via telnet to a redis server and write sync\r\n wait for the the payload to be written and then write any command (valid or invalid), such as ping\r\n on the telnet connection. It'll crash the server. --- src/networking.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/networking.c b/src/networking.c index 2fa05f0d6..20d05a9e3 100644 --- a/src/networking.c +++ b/src/networking.c @@ -356,6 +356,17 @@ void _addReplyProtoToList(client *c, const char *s, size_t len) { void _addReplyToBufferOrList(client *c, const char *s, size_t len) { if (c->flags & CLIENT_CLOSE_AFTER_REPLY) return; + /* 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) { + serverLog(LL_WARNING, "Replica generated a reply to command %s, disconnecting it", + c->lastcmd ? c->lastcmd->name : ""); + freeClientAsync(c); + return; + } + size_t reply_len = _addReplyToBuffer(c,s,len); if (len > reply_len) _addReplyProtoToList(c,s+reply_len,len-reply_len); }