Skip to content

Commit 93421f9

Browse files
justinbrewermichael-grunder
authored andcommitted
Properly detect integer parse errors
Badly formatted or out-of-range integers are now protocol errors. Signed-off-by: Justin Brewer <jzb0012@auburn.edu>
1 parent dbde4f6 commit 93421f9

File tree

2 files changed

+77
-29
lines changed

2 files changed

+77
-29
lines changed

read.c

+39-29
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include <assert.h>
4040
#include <errno.h>
4141
#include <ctype.h>
42+
#include <limits.h>
4243

4344
#include "read.h"
4445
#include "sds.h"
@@ -142,32 +143,24 @@ static char *seekNewline(char *s, size_t len) {
142143
}
143144

144145
/* Read a long long value starting at *s, under the assumption that it will be
145-
* terminated by \r\n. Ambiguously returns -1 for unexpected input. */
146-
static long long readLongLong(char *s) {
147-
long long v = 0;
148-
int dec, mult = 1;
149-
char c;
150-
151-
if (*s == '-') {
152-
mult = -1;
153-
s++;
154-
} else if (*s == '+') {
155-
mult = 1;
156-
s++;
146+
* terminated by \r\n. Returns REDIS_ERR for unexpected inputs or if the
147+
* resulting value would be greater than LLONG_MAX. */
148+
static int readLongLong(char *s, long long* val) {
149+
char* end;
150+
errno = 0;
151+
152+
long long v = strtoll(s, &end, 10);
153+
154+
if(s == end || ((v == LLONG_MAX || v == LLONG_MIN) && errno == ERANGE)
155+
|| (*end != '\r' || *(end+1) != '\n')) {
156+
return REDIS_ERR;
157157
}
158158

159-
while ((c = *(s++)) != '\r') {
160-
dec = c - '0';
161-
if (dec >= 0 && dec < 10) {
162-
v *= 10;
163-
v += dec;
164-
} else {
165-
/* Should not happen... */
166-
return -1;
167-
}
159+
if(val) {
160+
*val = v;
168161
}
169162

170-
return mult*v;
163+
return REDIS_OK;
171164
}
172165

173166
static char *readLine(redisReader *r, int *_len) {
@@ -218,10 +211,17 @@ static int processLineItem(redisReader *r) {
218211

219212
if ((p = readLine(r,&len)) != NULL) {
220213
if (cur->type == REDIS_REPLY_INTEGER) {
221-
if (r->fn && r->fn->createInteger)
222-
obj = r->fn->createInteger(cur,readLongLong(p));
223-
else
214+
if (r->fn && r->fn->createInteger) {
215+
long long v;
216+
if(readLongLong(p, &v) == REDIS_ERR) {
217+
__redisReaderSetError(r,REDIS_ERR_PROTOCOL,
218+
"Bad integer value");
219+
return REDIS_ERR;
220+
}
221+
obj = r->fn->createInteger(cur,v);
222+
} else {
224223
obj = (void*)REDIS_REPLY_INTEGER;
224+
}
225225
} else {
226226
/* Type will be error or status. */
227227
if (r->fn && r->fn->createString)
@@ -248,7 +248,7 @@ static int processBulkItem(redisReader *r) {
248248
redisReadTask *cur = &(r->rstack[r->ridx]);
249249
void *obj = NULL;
250250
char *p, *s;
251-
long len;
251+
long long len;
252252
unsigned long bytelen;
253253
int success = 0;
254254

@@ -257,7 +257,12 @@ static int processBulkItem(redisReader *r) {
257257
if (s != NULL) {
258258
p = r->buf+r->pos;
259259
bytelen = s-(r->buf+r->pos)+2; /* include \r\n */
260-
len = readLongLong(p);
260+
261+
if (readLongLong(p, &len) == REDIS_ERR) {
262+
__redisReaderSetError(r,REDIS_ERR_PROTOCOL,
263+
"Bad bulk string length");
264+
return REDIS_ERR;
265+
}
261266

262267
if (len < 0) {
263268
/* The nil object can always be created. */
@@ -301,7 +306,7 @@ static int processMultiBulkItem(redisReader *r) {
301306
redisReadTask *cur = &(r->rstack[r->ridx]);
302307
void *obj;
303308
char *p;
304-
long elements;
309+
long long elements;
305310
int root = 0;
306311

307312
/* Set error for nested multi bulks with depth > 7 */
@@ -312,7 +317,12 @@ static int processMultiBulkItem(redisReader *r) {
312317
}
313318

314319
if ((p = readLine(r,NULL)) != NULL) {
315-
elements = readLongLong(p);
320+
if(readLongLong(p, &elements) == REDIS_ERR) {
321+
__redisReaderSetError(r,REDIS_ERR_PROTOCOL,
322+
"Bad multi-bulk length");
323+
return REDIS_ERR;
324+
}
325+
316326
root = (r->ridx == 0);
317327

318328
if (elements == -1) {

test.c

+38
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,44 @@ static void test_reply_reader(void) {
302302
strncasecmp(reader->errstr,"No support for",14) == 0);
303303
redisReaderFree(reader);
304304

305+
test("Correctly parses LLONG_MAX: ");
306+
reader = redisReaderCreate();
307+
redisReaderFeed(reader, ":9223372036854775807\r\n",22);
308+
ret = redisReaderGetReply(reader,&reply);
309+
test_cond(ret == REDIS_OK &&
310+
((redisReply*)reply)->type == REDIS_REPLY_INTEGER &&
311+
((redisReply*)reply)->integer == LLONG_MAX);
312+
freeReplyObject(reply);
313+
redisReaderFree(reader);
314+
315+
test("Set error when > LLONG_MAX: ");
316+
reader = redisReaderCreate();
317+
redisReaderFeed(reader, ":9223372036854775808\r\n",22);
318+
ret = redisReaderGetReply(reader,&reply);
319+
test_cond(ret == REDIS_ERR &&
320+
strcasecmp(reader->errstr,"Bad integer value") == 0);
321+
freeReplyObject(reply);
322+
redisReaderFree(reader);
323+
324+
test("Correctly parses LLONG_MIN: ");
325+
reader = redisReaderCreate();
326+
redisReaderFeed(reader, ":-9223372036854775808\r\n",23);
327+
ret = redisReaderGetReply(reader,&reply);
328+
test_cond(ret == REDIS_OK &&
329+
((redisReply*)reply)->type == REDIS_REPLY_INTEGER &&
330+
((redisReply*)reply)->integer == LLONG_MIN);
331+
freeReplyObject(reply);
332+
redisReaderFree(reader);
333+
334+
test("Set error when < LLONG_MIN: ");
335+
reader = redisReaderCreate();
336+
redisReaderFeed(reader, ":-9223372036854775809\r\n",23);
337+
ret = redisReaderGetReply(reader,&reply);
338+
test_cond(ret == REDIS_ERR &&
339+
strcasecmp(reader->errstr,"Bad integer value") == 0);
340+
freeReplyObject(reply);
341+
redisReaderFree(reader);
342+
305343
test("Works with NULL functions for reply: ");
306344
reader = redisReaderCreate();
307345
reader->fn = NULL;

0 commit comments

Comments
 (0)