summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Nunberg <mnunberg@haskalah.org>2019-02-11 14:52:37 -0500
committerMark Nunberg <mnunberg@haskalah.org>2019-02-20 09:11:10 -0500
commit24e6166fedbd76a47db2ca0b4f7ab52edc0044e8 (patch)
treec84866cb328ca4440f100b54480f53cd6b5a4b32
parent5f633ac4ec0ee818fb9785e751fbbdfab48f9542 (diff)
libevent: fix invalid mem access on delete within callback enter
-rw-r--r--adapters/libevent.h41
1 files changed, 37 insertions, 4 deletions
diff --git a/adapters/libevent.h b/adapters/libevent.h
index 5959e89..58bab4b 100644
--- a/adapters/libevent.h
+++ b/adapters/libevent.h
@@ -34,26 +34,49 @@
#include "../hiredis.h"
#include "../async.h"
+#define REDIS_LIBEVENT_DELETED 0x01
+#define REDIS_LIBEVENT_ENTERED 0x02
+
typedef struct redisLibeventEvents {
redisAsyncContext *context;
struct event *ev, *tmr;
struct event_base *base;
struct timeval tv;
short flags;
+ short state;
} redisLibeventEvents;
+static void redisLibeventDestroy(redisLibeventEvents *e) {
+ free(e);
+}
+
static void redisLibeventHandler(int fd, short event, void *arg) {
((void)fd);
redisLibeventEvents *e = (redisLibeventEvents*)arg;
- if (event & EV_TIMEOUT) {
+ e->state |= REDIS_LIBEVENT_ENTERED;
+
+ #define CHECK_DELETED() if (e->state & REDIS_LIBEVENT_DELETED) {\
+ redisLibeventDestroy(e);\
+ return; \
+ }
+
+ if ((event & EV_TIMEOUT) && (e->state & REDIS_LIBEVENT_DELETED) == 0) {
redisAsyncHandleTimeout(e->context);
+ CHECK_DELETED();
}
- if ((event & EV_READ) && e->context) {
+
+ if ((event & EV_READ) && e->context && (e->state & REDIS_LIBEVENT_DELETED) == 0) {
redisAsyncHandleRead(e->context);
+ CHECK_DELETED();
}
- if ((event & EV_WRITE) && e->context) {
+
+ if ((event & EV_WRITE) && e->context && (e->state & REDIS_LIBEVENT_DELETED) == 0) {
redisAsyncHandleWrite(e->context);
+ CHECK_DELETED();
}
+
+ e->state &= ~REDIS_LIBEVENT_ENTERED;
+ #undef CHECK_DELETED
}
static void redisLibeventUpdate(void *privdata, short flag, int isRemove) {
@@ -98,8 +121,18 @@ static void redisLibeventDelWrite(void *privdata) {
static void redisLibeventCleanup(void *privdata) {
redisLibeventEvents *e = (redisLibeventEvents*)privdata;
+ if (!e) {
+ return;
+ }
+ event_del(e->ev);
event_free(e->ev);
- free(e);
+ e->ev = NULL;
+
+ if (e->state & REDIS_LIBEVENT_ENTERED) {
+ e->state |= REDIS_LIBEVENT_DELETED;
+ } else {
+ free(e);
+ }
}
static void redisLibeventSetTimeout(void *privdata, struct timeval tv) {