From 51510ddc86b8a3ac36c98d9cdd246c54b288d374 Mon Sep 17 00:00:00 2001 From: Hou Tao Date: Sat, 28 Oct 2017 20:58:26 +0800 Subject: [PATCH 7/8] epoll: prevent the double-free of epi in eventpoll_release_file() After the removal of epmutex, it's possible eventpoll_release_file() and ep_free() will try to remove the same epi. The removal of epi is protected by ep->mtx, so there are two possible sequences: (1) eventpoll_release_file() first, then ep_free() there is no double-free, because after the invocation of eventpoll_release_file(), the epi will be removed from ep->rbr and ep_free() will not be able to free it. (2) ep_free() first, then eventpoll_release_file() double-free is possible because before the invocation of ep_remove() in ep_free() eventpoll_release_file may has already got the epi from file->f_ep_links. To protect against the double-free case, check rb_first_cached() in eventpoll_release_file() to ensure the epi has not been removed by ep_free() Signed-off-by: Hou Tao Signed-off-by: Wei Li --- fs/eventpoll.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/fs/eventpoll.c b/fs/eventpoll.c index b33d73556c65..9d1714058a3a 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -1045,13 +1045,22 @@ void eventpoll_release_file(struct file *file) break; ep = eventpoll_get_ep(epi->ep); - /* Current epi has been removed by ep_free() */ + /* Current epi had been removed by ep_free() */ if (!ep) continue; rcu_read_unlock(); mutex_lock_nested(&ep->mtx, 0); - ep_remove(ep, epi); + /* + * If rb_first_cached() returns NULL, it means that the current epi + * had been removed by ep_free(). To prevent epi from double-freeing, + * check the condition before invoking ep_remove(). + * If eventpoll_release_file() frees epi firstly, the epi will not + * be freed again because the epi must have been removed from ep->rbr + * when ep_free() is invoked. + */ + if (rb_first_cached(&ep->rbr)) + ep_remove(ep, epi); mutex_unlock(&ep->mtx); eventpoll_put_ep(ep); -- 2.17.1