diff options
author | Stefan Metzmacher <metze@samba.org> | 2013-02-15 16:33:56 +0100 |
---|---|---|
committer | Jeremy Allison <jra@samba.org> | 2013-03-01 12:00:15 -0800 |
commit | 73dabddbdce99c5bc19a4be814f88da581e6d372 (patch) | |
tree | eaac27f865324216d79654bd224f27118840d959 | |
parent | 2b05c9621784da8e5aa793cd2110dde768084968 (diff) | |
download | samba-73dabddbdce99c5bc19a4be814f88da581e6d372.tar.gz samba-73dabddbdce99c5bc19a4be814f88da581e6d372.tar.bz2 samba-73dabddbdce99c5bc19a4be814f88da581e6d372.zip |
tevent: avoid any operation on epoll_ev after a epoll_panic()
This calls TALLOC_FREE(ev->additional_data), which is epoll_ev
within epoll_panic() before calling the fallback handler.
In order to notice that a epoll_panic() happened, a caller can
register a pointer to a bool variable under epoll_ev->panic_state.
As epoll_check_reopen() can fail due to a epoll_panic(),
we need to force the replay flag if we have called any event handler.
Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
-rw-r--r-- | lib/tevent/tevent_epoll.c | 85 |
1 files changed, 74 insertions, 11 deletions
diff --git a/lib/tevent/tevent_epoll.c b/lib/tevent/tevent_epoll.c index 4c16aec5d6..6ad7b5b3e7 100644 --- a/lib/tevent/tevent_epoll.c +++ b/lib/tevent/tevent_epoll.c @@ -40,6 +40,8 @@ struct epoll_event_context { pid_t pid; + bool panic_force_replay; + bool *panic_state; bool (*panic_fallback)(struct tevent_context *ev, bool replay); }; @@ -134,8 +136,21 @@ static void epoll_panic(struct epoll_event_context *epoll_ev, const char *reason, bool replay) { struct tevent_context *ev = epoll_ev->ev; + bool (*panic_fallback)(struct tevent_context *ev, bool replay); - if (epoll_ev->panic_fallback == NULL) { + panic_fallback = epoll_ev->panic_fallback; + + if (epoll_ev->panic_state != NULL) { + *epoll_ev->panic_state = true; + } + + if (epoll_ev->panic_force_replay) { + replay = true; + } + + TALLOC_FREE(ev->additional_data); + + if (panic_fallback == NULL) { tevent_debug(ev, TEVENT_DEBUG_FATAL, "%s (%s) replay[%u] - calling abort()\n", reason, strerror(errno), (unsigned)replay); @@ -146,7 +161,7 @@ static void epoll_panic(struct epoll_event_context *epoll_ev, "%s (%s) replay[%u] - calling panic_fallback\n", reason, strerror(errno), (unsigned)replay); - if (!epoll_ev->panic_fallback(ev, replay)) { + if (!panic_fallback(ev, replay)) { /* Fallback failed. */ tevent_debug(ev, TEVENT_DEBUG_FATAL, "%s (%s) replay[%u] - calling abort()\n", @@ -209,6 +224,8 @@ static void epoll_add_event(struct epoll_event_context *epoll_ev, struct tevent_ static void epoll_check_reopen(struct epoll_event_context *epoll_ev) { struct tevent_fd *fde; + bool *caller_panic_state = epoll_ev->panic_state; + bool panic_triggered = false; if (epoll_ev->pid == getpid()) { return; @@ -217,8 +234,7 @@ static void epoll_check_reopen(struct epoll_event_context *epoll_ev) close(epoll_ev->epoll_fd); epoll_ev->epoll_fd = epoll_create(64); if (epoll_ev->epoll_fd == -1) { - tevent_debug(epoll_ev->ev, TEVENT_DEBUG_FATAL, - "Failed to recreate epoll handle after fork\n"); + epoll_panic(epoll_ev, "epoll_create() failed", false); return; } @@ -228,9 +244,17 @@ static void epoll_check_reopen(struct epoll_event_context *epoll_ev) } epoll_ev->pid = getpid(); + epoll_ev->panic_state = &panic_triggered; for (fde=epoll_ev->ev->fd_events;fde;fde=fde->next) { epoll_add_event(epoll_ev, fde); + if (panic_triggered) { + if (caller_panic_state != NULL) { + *caller_panic_state = true; + } + return; + } } + epoll_ev->panic_state = NULL; } #define EPOLL_ADDITIONAL_FD_FLAG_HAS_EVENT (1<<0) @@ -463,15 +487,33 @@ static int epoll_event_fd_destructor(struct tevent_fd *fde) { struct tevent_context *ev = fde->event_ctx; struct epoll_event_context *epoll_ev = NULL; + bool panic_triggered = false; - if (ev) { - epoll_ev = talloc_get_type(ev->additional_data, - struct epoll_event_context); + if (ev == NULL) { + return tevent_common_fd_destructor(fde); + } - epoll_check_reopen(epoll_ev); + epoll_ev = talloc_get_type_abort(ev->additional_data, + struct epoll_event_context); - epoll_del_event(epoll_ev, fde); + /* + * we must remove the event from the list + * otherwise a panic fallback handler may + * reuse invalid memory + */ + DLIST_REMOVE(ev->fd_events, fde); + + epoll_ev->panic_state = &panic_triggered; + epoll_check_reopen(epoll_ev); + if (panic_triggered) { + return tevent_common_fd_destructor(fde); + } + + epoll_del_event(epoll_ev, fde); + if (panic_triggered) { + return tevent_common_fd_destructor(fde); } + epoll_ev->panic_state = NULL; return tevent_common_fd_destructor(fde); } @@ -490,8 +532,7 @@ static struct tevent_fd *epoll_event_add_fd(struct tevent_context *ev, TALLOC_CT struct epoll_event_context *epoll_ev = talloc_get_type(ev->additional_data, struct epoll_event_context); struct tevent_fd *fde; - - epoll_check_reopen(epoll_ev); + bool panic_triggered = false; fde = tevent_common_add_fd(ev, mem_ctx, fd, flags, handler, private_data, @@ -500,6 +541,13 @@ static struct tevent_fd *epoll_event_add_fd(struct tevent_context *ev, TALLOC_CT talloc_set_destructor(fde, epoll_event_fd_destructor); + epoll_ev->panic_state = &panic_triggered; + epoll_check_reopen(epoll_ev); + if (panic_triggered) { + return fde; + } + epoll_ev->panic_state = NULL; + epoll_add_event(epoll_ev, fde); return fde; @@ -512,6 +560,7 @@ static void epoll_event_set_fd_flags(struct tevent_fd *fde, uint16_t flags) { struct tevent_context *ev; struct epoll_event_context *epoll_ev; + bool panic_triggered = false; if (fde->flags == flags) return; @@ -520,7 +569,12 @@ static void epoll_event_set_fd_flags(struct tevent_fd *fde, uint16_t flags) fde->flags = flags; + epoll_ev->panic_state = &panic_triggered; epoll_check_reopen(epoll_ev); + if (panic_triggered) { + return; + } + epoll_ev->panic_state = NULL; epoll_change_event(epoll_ev, fde); } @@ -533,6 +587,7 @@ static int epoll_event_loop_once(struct tevent_context *ev, const char *location struct epoll_event_context *epoll_ev = talloc_get_type(ev->additional_data, struct epoll_event_context); struct timeval tval; + bool panic_triggered = false; if (ev->signal_events && tevent_common_check_signal(ev)) { @@ -549,7 +604,15 @@ static int epoll_event_loop_once(struct tevent_context *ev, const char *location return 0; } + epoll_ev->panic_state = &panic_triggered; + epoll_ev->panic_force_replay = true; epoll_check_reopen(epoll_ev); + if (panic_triggered) { + errno = EINVAL; + return -1; + } + epoll_ev->panic_force_replay = false; + epoll_ev->panic_state = NULL; return epoll_event_loop(epoll_ev, &tval); } |