diff options
author | Binbin <binloveplay1314@qq.com> | 2022-01-10 14:21:16 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-01-10 08:21:16 +0200 |
commit | a1ae260e8addad04e1a73348c8f7bfeab398c2a9 (patch) | |
tree | 6ff04fbc9030b4b7870e594c6249b5970827205b /src/networking.c | |
parent | d7479107c742ac41cbe66480fa005c102abba588 (diff) | |
download | redis-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.c | 36 |
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() |