libevent: fix invalid mem access on delete within callback enter

This commit is contained in:
Mark Nunberg 2019-02-11 14:52:37 -05:00
parent 5f633ac4ec
commit 24e6166fed

View File

@ -34,26 +34,49 @@
#include "../hiredis.h" #include "../hiredis.h"
#include "../async.h" #include "../async.h"
#define REDIS_LIBEVENT_DELETED 0x01
#define REDIS_LIBEVENT_ENTERED 0x02
typedef struct redisLibeventEvents { typedef struct redisLibeventEvents {
redisAsyncContext *context; redisAsyncContext *context;
struct event *ev, *tmr; struct event *ev, *tmr;
struct event_base *base; struct event_base *base;
struct timeval tv; struct timeval tv;
short flags; short flags;
short state;
} redisLibeventEvents; } redisLibeventEvents;
static void redisLibeventDestroy(redisLibeventEvents *e) {
free(e);
}
static void redisLibeventHandler(int fd, short event, void *arg) { static void redisLibeventHandler(int fd, short event, void *arg) {
((void)fd); ((void)fd);
redisLibeventEvents *e = (redisLibeventEvents*)arg; 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); 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); 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); redisAsyncHandleWrite(e->context);
CHECK_DELETED();
} }
e->state &= ~REDIS_LIBEVENT_ENTERED;
#undef CHECK_DELETED
} }
static void redisLibeventUpdate(void *privdata, short flag, int isRemove) { static void redisLibeventUpdate(void *privdata, short flag, int isRemove) {
@ -98,8 +121,18 @@ static void redisLibeventDelWrite(void *privdata) {
static void redisLibeventCleanup(void *privdata) { static void redisLibeventCleanup(void *privdata) {
redisLibeventEvents *e = (redisLibeventEvents*)privdata; redisLibeventEvents *e = (redisLibeventEvents*)privdata;
if (!e) {
return;
}
event_del(e->ev);
event_free(e->ev); event_free(e->ev);
e->ev = NULL;
if (e->state & REDIS_LIBEVENT_ENTERED) {
e->state |= REDIS_LIBEVENT_DELETED;
} else {
free(e); free(e);
}
} }
static void redisLibeventSetTimeout(void *privdata, struct timeval tv) { static void redisLibeventSetTimeout(void *privdata, struct timeval tv) {