[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <1509195507-29037-8-git-send-email-houtao1@huawei.com>
Date: Sat, 28 Oct 2017 20:58:26 +0800
From: Hou Tao <houtao1@...wei.com>
To: <linux-fsdevel@...r.kernel.org>
CC: <linux-kernel@...r.kernel.org>, <viro@...iv.linux.org.uk>,
<jbaron@...mai.com>, <oleg@...hat.com>, <dave@...olabs.net>,
<koct9i@...il.com>
Subject: [RFC][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 <houtao1@...wei.com>
---
fs/eventpoll.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index 27b743b..cd7a9f4 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -1058,13 +1058,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.7.5
Powered by blists - more mailing lists