From 8e0264cfd6889b73c241b60736fe96ba1322ee6e Mon Sep 17 00:00:00 2001 From: Michael Grunder Date: Fri, 22 May 2020 09:27:49 -0700 Subject: Allow users to replace allocator and handle OOM everywhere. (#800) * Adds an indirection to every allocation/deallocation to allow users to plug in ones of their choosing (use custom functions, jemalloc, etc). * Gracefully handle OOM everywhere in hiredis. This should make it possible for users of the library to have more flexibility in how they handle such situations. * Changes `redisReaderTask->elements` from an `int` to a `long long` to prevent a possible overflow when transferring the task elements into a `redisReply`. * Adds a configurable `max elements` member to `redisReader` that defaults to 2^32 - 1. This can be set to "unlimited" by setting the value to zero. --- read.c | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) (limited to 'read.c') diff --git a/read.c b/read.c index 4924014..d10b62a 100644 --- a/read.c +++ b/read.c @@ -42,6 +42,7 @@ #include #include +#include "alloc.h" #include "read.h" #include "sds.h" #include "win32.h" @@ -425,7 +426,7 @@ static int redisReaderGrow(redisReader *r) { /* Grow our stack size */ newlen = r->tasks + REDIS_READER_STACK_SIZE; - aux = realloc(r->task, sizeof(*r->task) * newlen); + aux = hi_realloc(r->task, sizeof(*r->task) * newlen); if (aux == NULL) goto oom; @@ -433,7 +434,7 @@ static int redisReaderGrow(redisReader *r) { /* Allocate new tasks */ for (; r->tasks < newlen; r->tasks++) { - r->task[r->tasks] = calloc(1, sizeof(**r->task)); + r->task[r->tasks] = hi_calloc(1, sizeof(**r->task)); if (r->task[r->tasks] == NULL) goto oom; } @@ -467,7 +468,9 @@ static int processAggregateItem(redisReader *r) { root = (r->ridx == 0); - if (elements < -1 || (LLONG_MAX > SIZE_MAX && elements > SIZE_MAX)) { + if (elements < -1 || (LLONG_MAX > SIZE_MAX && elements > SIZE_MAX) || + (r->maxelements > 0 && elements > r->maxelements)) + { __redisReaderSetError(r,REDIS_ERR_PROTOCOL, "Multi-bulk length out of range"); return REDIS_ERR; @@ -602,7 +605,7 @@ static int processItem(redisReader *r) { redisReader *redisReaderCreateWithFunctions(redisReplyObjectFunctions *fn) { redisReader *r; - r = calloc(1,sizeof(redisReader)); + r = hi_calloc(1,sizeof(redisReader)); if (r == NULL) return NULL; @@ -610,22 +613,22 @@ redisReader *redisReaderCreateWithFunctions(redisReplyObjectFunctions *fn) { if (r->buf == NULL) goto oom; - r->task = calloc(REDIS_READER_STACK_SIZE, sizeof(*r->task)); + r->task = hi_calloc(REDIS_READER_STACK_SIZE, sizeof(*r->task)); if (r->task == NULL) goto oom; for (; r->tasks < REDIS_READER_STACK_SIZE; r->tasks++) { - r->task[r->tasks] = calloc(1, sizeof(**r->task)); + r->task[r->tasks] = hi_calloc(1, sizeof(**r->task)); if (r->task[r->tasks] == NULL) goto oom; } r->fn = fn; r->maxbuf = REDIS_READER_MAX_BUF; - + r->maxelements = REDIS_READER_MAX_ARRAY_ELEMENTS; r->ridx = -1; - return r; + return r; oom: redisReaderFree(r); return NULL; @@ -640,14 +643,14 @@ void redisReaderFree(redisReader *r) { /* We know r->task[i] is allocatd if i < r->tasks */ for (int i = 0; i < r->tasks; i++) { - free(r->task[i]); + hi_free(r->task[i]); } if (r->task) - free(r->task); + hi_free(r->task); sdsfree(r->buf); - free(r); + hi_free(r); } int redisReaderFeed(redisReader *r, const char *buf, size_t len) { @@ -663,23 +666,22 @@ int redisReaderFeed(redisReader *r, const char *buf, size_t len) { if (r->len == 0 && r->maxbuf != 0 && sdsavail(r->buf) > r->maxbuf) { sdsfree(r->buf); r->buf = sdsempty(); - r->pos = 0; + if (r->buf == 0) goto oom; - /* r->buf should not be NULL since we just free'd a larger one. */ - assert(r->buf != NULL); + r->pos = 0; } newbuf = sdscatlen(r->buf,buf,len); - if (newbuf == NULL) { - __redisReaderSetErrorOOM(r); - return REDIS_ERR; - } + if (newbuf == NULL) goto oom; r->buf = newbuf; r->len = sdslen(r->buf); } return REDIS_OK; +oom: + __redisReaderSetErrorOOM(r); + return REDIS_ERR; } int redisReaderGetReply(redisReader *r, void **reply) { -- cgit v1.2.3