summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--memcached.c41
-rw-r--r--testapp.c80
2 files changed, 120 insertions, 1 deletions
diff --git a/memcached.c b/memcached.c
index 97d94f3..fed0cc1 100644
--- a/memcached.c
+++ b/memcached.c
@@ -45,6 +45,7 @@
#include <assert.h>
#include <limits.h>
#include <sysexits.h>
+#include <stddef.h>
/* FreeBSD 4.x doesn't have IOV_MAX exposed. */
#ifndef IOV_MAX
@@ -1416,7 +1417,45 @@ static void bin_read_key(conn *c, enum bin_substates next_substate, int extra) {
assert(c);
c->substate = next_substate;
c->rlbytes = c->keylen + extra;
- assert(c->rsize >= c->rlbytes);
+
+ /* Ok... do we have room for the extras and the key in the input buffer? */
+ ptrdiff_t offset = c->rcurr + sizeof(protocol_binary_request_header) - c->rbuf;
+ if (c->rlbytes > c->rsize - offset) {
+ size_t nsize = c->rsize;
+ size_t size = c->rlbytes + sizeof(protocol_binary_request_header);
+
+ while (size > nsize) {
+ nsize *= 2;
+ }
+
+ if (nsize != c->rsize) {
+ if (settings.verbose) {
+ fprintf(stderr, "%d: Need to grow buffer from %lu to %lu\n",
+ c->sfd, (unsigned long)c->rsize, (unsigned long)nsize);
+ }
+ char *newm = realloc(c->rbuf, nsize);
+ if (newm == NULL) {
+ if (settings.verbose) {
+ fprintf(stderr, "%d: Failed to grow buffer.. closing connection\n",
+ c->sfd);
+ }
+ conn_set_state(c, conn_closing);
+ return;
+ }
+
+ /* rcurr should point to the same offset in the packet */
+ c->rcurr = c->rbuf + offset - sizeof(protocol_binary_request_header);
+ c->rsize = nsize;
+ }
+ if (c->rbuf != c->rcurr) {
+ memmove(c->rbuf, c->rcurr, c->rbytes);
+ c->rcurr = c->rbuf;
+ if (settings.verbose) {
+ fprintf(stderr, "%d: Repack input buffer\n", c->sfd);
+ }
+ }
+ }
+
/* preserve the header in the buffer.. */
c->ritem = c->rcurr + sizeof(protocol_binary_request_header);
conn_set_state(c, conn_nread);
diff --git a/testapp.c b/testapp.c
index 3316968..7c3d3e1 100644
--- a/testapp.c
+++ b/testapp.c
@@ -1,5 +1,11 @@
/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
#undef NDEBUG
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
@@ -11,6 +17,7 @@
#include <unistd.h>
#include <netinet/in.h>
+#include "protocol_binary.h"
#include "config.h"
#include "cache.h"
#include "util.h"
@@ -333,6 +340,78 @@ static enum test_return test_issue_44(void) {
return TEST_PASS;
}
+static struct addrinfo *lookuphost(const char *hostname, in_port_t port)
+{
+ struct addrinfo *ai = 0;
+ struct addrinfo hints = { .ai_family = AF_UNSPEC,
+ .ai_protocol = IPPROTO_TCP,
+ .ai_socktype = SOCK_STREAM };
+ char service[NI_MAXSERV];
+ int error;
+
+ (void)snprintf(service, NI_MAXSERV, "%d", port);
+ if ((error = getaddrinfo(hostname, service, &hints, &ai)) != 0) {
+ if (error != EAI_SYSTEM) {
+ fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(error));
+ } else {
+ perror("getaddrinfo()");
+ }
+ }
+
+ return ai;
+}
+
+static int connect_server(const char *hostname, in_port_t port)
+{
+ struct addrinfo *ai = lookuphost(hostname, port);
+ int sock = -1;
+ if (ai != NULL) {
+ if ((sock = socket(ai->ai_family, ai->ai_socktype,
+ ai->ai_protocol)) != -1) {
+ if (connect(sock, ai->ai_addr, ai->ai_addrlen) == -1) {
+ fprintf(stderr, "Failed to connect socket: %s\n",
+ strerror(errno));
+ close(sock);
+ sock = -1;
+ }
+ } else {
+ fprintf(stderr, "Failed to create socket: %s\n", strerror(errno));
+ }
+
+ freeaddrinfo(ai);
+ }
+ return sock;
+}
+
+
+static enum test_return test_issue_72(void) {
+ in_port_t port;
+ pid_t pid = start_server(&port, false);
+ int sock = connect_server("127.0.0.1", port);
+ assert(sock != -1);
+
+ char data[sizeof(protocol_binary_request_set) + 2048] = { 0 };
+ protocol_binary_request_set *request = (protocol_binary_request_set*)data;
+ request->message.header.request.magic = PROTOCOL_BINARY_REQ;
+ request->message.header.request.opcode = PROTOCOL_BINARY_CMD_SET;
+ uint16_t keylen = 2048;
+ request->message.header.request.keylen = htons(keylen);
+ request->message.header.request.extlen = 8;
+ request->message.header.request.bodylen = htonl(keylen + 8);
+
+ assert(write(sock, data, 2000) == 2000);
+ usleep(250);
+ assert(write(sock, data, sizeof(data) - 2000) == sizeof(data) - 2000);
+
+ protocol_binary_response_set response;
+ assert(read(sock, &response, sizeof(response)) == sizeof(response));
+ assert(response.message.header.response.magic == PROTOCOL_BINARY_RES);
+ assert(response.message.header.response.status == PROTOCOL_BINARY_RESPONSE_SUCCESS);
+ close(sock);
+ assert(kill(pid, SIGTERM) == 0);
+ return TEST_PASS;
+}
+
typedef enum test_return (*TEST_FUNC)(void);
struct testcase {
const char *description;
@@ -351,6 +430,7 @@ struct testcase testcases[] = {
{ "strtoul", test_safe_strtoul },
{ "strtoull", test_safe_strtoull },
{ "issue_44", test_issue_44 },
+ { "issue_72", test_issue_72 },
{ NULL, NULL }
};