Skip to content

Commit 76a6c36

Browse files
committed
proxy: fix buffer overflow with multiget syntax
"get[200 spaces]key1 key2\r\n" would overflow a temporary buffer used to process multiget syntax. To exploit this you must first pass the check in try_read_command_proxy: - The request before the first newline must be less than 1024 bytes. - If it is more than 1024 bytes there is a limit of 100 spaces. - The key length is still checked at 250 bytes - Meaning you have up to 772 spaces and then the key to create stack corruption. So the amount of data you can shove in here isn't unlimited. The fix caps the amount of data pre-key to be reasonable. Something like GAT needs space for a 32bit TTL which is at most going to be 15 bytes + spaces, so we limit it to 20 bytes. I hate hate hate hate hate the multiget syntax. hate it.
1 parent bc11696 commit 76a6c36

File tree

2 files changed

+20
-2
lines changed

2 files changed

+20
-2
lines changed

Diff for: proto_proxy.c

+14-2
Original file line numberDiff line numberDiff line change
@@ -721,6 +721,12 @@ int proxy_run_coroutine(lua_State *Lc, mc_resp *resp, io_pending_proxy_t *p, con
721721
return 0;
722722
}
723723

724+
// basically any data before the first key.
725+
// max is like 15ish plus spaces. we can be more strict about how many spaces
726+
// to expect because any client spamming space is being deliberately stupid
727+
// anyway.
728+
#define MAX_CMD_PREFIX 20
729+
724730
static void proxy_process_command(conn *c, char *command, size_t cmdlen, bool multiget) {
725731
assert(c != NULL);
726732
LIBEVENT_THREAD *thr = c->thread;
@@ -793,12 +799,18 @@ static void proxy_process_command(conn *c, char *command, size_t cmdlen, bool mu
793799
if (!multiget && pr.cmd_type == CMD_TYPE_GET && pr.has_space) {
794800
uint32_t keyoff = pr.tokens[pr.keytoken];
795801
while (pr.klen != 0) {
796-
char temp[KEY_MAX_LENGTH + 30];
802+
char temp[KEY_MAX_LENGTH + MAX_CMD_PREFIX + 30];
797803
char *cur = temp;
798804
// Core daemon can abort the entire command if one key is bad, but
799805
// we cannot from the proxy. Instead we have to inject errors into
800806
// the stream. This should, thankfully, be rare at least.
801-
if (pr.klen > KEY_MAX_LENGTH) {
807+
if (pr.tokens[pr.keytoken] > MAX_CMD_PREFIX) {
808+
if (!resp_start(c)) {
809+
conn_set_state(c, conn_closing);
810+
return;
811+
}
812+
proxy_out_errstring(c->resp, PROXY_CLIENT_ERROR, "malformed request");
813+
} else if (pr.klen > KEY_MAX_LENGTH) {
802814
if (!resp_start(c)) {
803815
conn_set_state(c, conn_closing);
804816
return;

Diff for: t/proxyunits.t

+6
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,12 @@ sub proxy_test {
151151
print $ps "$_\r\n";
152152
is(scalar <$ps>, "CLIENT_ERROR parsing request\r\n", "$_ got CLIENT_ERROR for too few tokens");
153153
}
154+
155+
my $space = ' ' x 200;
156+
print $ps "get$space key key\r\n";
157+
is(scalar <$ps>, "CLIENT_ERROR malformed request\r\n");
158+
is(scalar <$ps>, "CLIENT_ERROR malformed request\r\n");
159+
is(scalar <$ps>, "END\r\n"); # god damn multiget syntax.
154160
}
155161

156162
# Basic test with a backend; write a request to the client socket, read it

0 commit comments

Comments
 (0)