From 16e31f8dce64323fdef6151a30756ce2bc770a88 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Thu, 20 Jun 2013 01:51:09 +0000 Subject: avoid leaks on epoll/kqueue resources exhaustion Simply releasing the descriptor triggering ENOSPC/ENOMEM errors from epoll_ctl and kevent is not good enough, as those descriptors may have other descriptors (e.g. files to be served) hanging off of them. --- cmogstored.h | 4 ++++ http.c | 25 +++++++++++++++++++++++++ http_put.c | 11 +---------- mgmt.c | 10 ++++++++++ queue_common.c | 18 ++++++++++++++++++ queue_epoll.c | 2 +- queue_kqueue.c | 2 +- 7 files changed, 60 insertions(+), 12 deletions(-) diff --git a/cmogstored.h b/cmogstored.h index c9e1f46..9a84a57 100644 --- a/cmogstored.h +++ b/cmogstored.h @@ -432,6 +432,7 @@ void mog_mgmt_post_accept(int fd, struct mog_svc *, union mog_sockaddr *, socklen_t); enum mog_next mog_mgmt_queue_step(struct mog_fd *) MOG_CHECK; void mog_mgmt_quit_step(struct mog_fd *); +void mog_mgmt_drop(struct mog_fd *); /* queue_epoll.c */ struct mog_queue * mog_queue_new(void); @@ -498,6 +499,8 @@ enum mog_next mog_http_queue_step(struct mog_fd *) MOG_CHECK; void mog_http_quit_step(struct mog_fd *); char *mog_http_path(struct mog_http *, char *buf); void mog_http_reset(struct mog_http *); +void mog_http_unlink_ftmp(struct mog_http *); +void mog_http_drop(struct mog_fd *); /* http_dav.c */ void mog_http_delete(struct mog_fd *, char *buf); @@ -529,6 +532,7 @@ int mog_mkpath_for(struct mog_svc *svc, char *path); /* queue_common.c */ struct mog_queue *mog_queue_init(int queue_fd); void mog_queue_stop(struct mog_queue *keep); +void mog_queue_drop(struct mog_fd *); /* fsck_queue.c */ bool mog_fsck_queue_ready(struct mog_fd *mfd) MOG_CHECK; diff --git a/http.c b/http.c index 74ca982..a4c2cb8 100644 --- a/http.c +++ b/http.c @@ -95,6 +95,31 @@ MOG_NOINLINE static void http_close(struct mog_fd *mfd) mog_fd_put(mfd); } +void mog_http_unlink_ftmp(struct mog_http *http) +{ + struct mog_file *file = &http->forward->as.file; + + if (!file->tmppath) + return; + + if (mog_unlink(http->svc, file->tmppath) != 0) + syslog(LOG_ERR, "Failed to unlink %s (in %s): %m", + file->tmppath, http->svc->docroot); +} + +/* called if epoll/kevent is out-of-space */ +void mog_http_drop(struct mog_fd *mfd) +{ + struct mog_http *http = &mfd->as.http; + + assert(http->forward != MOG_IOSTAT); + if (http->forward) { + mog_http_unlink_ftmp(http); + mog_file_close(http->forward); + } + http_close(mfd); +} + /* returns true if we can continue queue step, false if not */ static enum mog_next http_wbuf_in_progress(struct mog_http *http) { diff --git a/http_put.c b/http_put.c index f86113e..db9228d 100644 --- a/http_put.c +++ b/http_put.c @@ -14,18 +14,9 @@ static __thread struct { static void file_close_null(struct mog_http *http) { - struct mog_file *file; - if (http->forward == NULL) return; - - file = &http->forward->as.file; - - if (file->tmppath) { - if (mog_unlink(http->svc, file->tmppath) != 0) - syslog(LOG_ERR, "Failed to unlink %s (in %s): %m", - file->tmppath, http->svc->docroot); - } + mog_http_unlink_ftmp(http); mog_file_close(http->forward); http->forward = NULL; } diff --git a/mgmt.c b/mgmt.c index 9909d27..3468e24 100644 --- a/mgmt.c +++ b/mgmt.c @@ -82,6 +82,16 @@ MOG_NOINLINE static void mgmt_close(struct mog_fd *mfd) mog_fd_put(mfd); } +/* called if epoll/kevent is out-of-space */ +void mog_mgmt_drop(struct mog_fd *mfd) +{ + struct mog_mgmt *mgmt = &mfd->as.mgmt; + + if (mgmt->forward && mgmt->forward != MOG_IOSTAT) + mog_file_close(mgmt->forward); + mgmt_close(mfd); +} + void mog_mgmt_writev(struct mog_mgmt *mgmt, struct iovec *iov, int iovcnt) { struct mog_fd *mfd = mog_fd_of(mgmt); diff --git a/queue_common.c b/queue_common.c index 79a5869..b9c2f99 100644 --- a/queue_common.c +++ b/queue_common.c @@ -46,3 +46,21 @@ void mog_queue_stop(struct mog_queue *keep) mog_fd_put(mfd); } } + +void mog_queue_drop(struct mog_fd *mfd) +{ + switch (mfd->fd_type) { + case MOG_FD_TYPE_HTTP: + case MOG_FD_TYPE_HTTPGET: + mog_http_drop(mfd); + return; + case MOG_FD_TYPE_MGMT: + mog_mgmt_drop(mfd); + return; + default: + syslog(LOG_ERR, + "dropping fd_type=%d, functionality may be compromised", + mfd->fd_type); + mog_fd_put(mfd); + } +} diff --git a/queue_epoll.c b/queue_epoll.c index aaa30f6..e6d36d5 100644 --- a/queue_epoll.c +++ b/queue_epoll.c @@ -160,7 +160,7 @@ epoll_ctl_error(struct mog_queue *q, struct mog_fd *mfd) case ENOMEM: case ENOSPC: syslog(LOG_ERR, "epoll_ctl: %m, dropping file descriptor"); - mog_fd_put(mfd); + mog_queue_drop(mfd); return; default: syslog(LOG_ERR, "unhandled epoll_ctl() error: %m"); diff --git a/queue_kqueue.c b/queue_kqueue.c index 020b339..6254984 100644 --- a/queue_kqueue.c +++ b/queue_kqueue.c @@ -86,7 +86,7 @@ kevent_add_error(struct mog_queue *q, struct mog_fd *mfd) case ENOMEM: syslog(LOG_ERR, "kevent(EV_ADD) out-of-space, dropping file descriptor"); - mog_fd_put(mfd); + mog_queue_drop(mfd); return; default: syslog(LOG_ERR, "unhandled kevent(EV_ADD) error: %m"); -- cgit v1.2.3-24-ge0c7