[PATCH 7.0 0864/1146] eventpoll: kill __ep_remove()
From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Date: 2026-05-20 17:03:34
Also in:
stable
Subsystem:
filesystems (vfs and infrastructure), the rest · Maintainers:
Alexander Viro, Christian Brauner, Linus Torvalds
7.0-stable review patch. If anyone has any objections, please let me know. ------------------ From: Christian Brauner <brauner@kernel.org> [ Upstream commit e9e5cd40d7c403e19f21d0f7b8b8ba3a76b58330 ] Remove the boolean conditional in __ep_remove() and restructure the code so the check for racing with eventpoll_release_file() are only done in the ep_remove_safe() path where they belong. Link: https://patch.msgid.link/20260423-work-epoll-uaf-v1-3-2470f9eec0f5@kernel.org Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org> Stable-dep-of: a6dc643c6931 ("eventpoll: fix ep_remove struct eventpoll / struct file UAF") Signed-off-by: Sasha Levin <sashal@kernel.org> --- fs/eventpoll.c | 67 ++++++++++++++++++++++---------------------------- 1 file changed, 30 insertions(+), 37 deletions(-)
diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index 99188c30fe6c7..c45995e790cfb 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c@@ -826,49 +826,18 @@ static void ep_free(struct eventpoll *ep) kfree_rcu(ep, rcu); } -static void __ep_remove_file(struct eventpoll *ep, struct epitem *epi, struct file *file); -static bool __ep_remove_epi(struct eventpoll *ep, struct epitem *epi); - -/* - * Removes a "struct epitem" from the eventpoll RB tree and deallocates - * all the associated resources. Must be called with "mtx" held. - * If the dying flag is set, do the removal only if force is true. - * This prevents ep_clear_and_put() from dropping all the ep references - * while running concurrently with eventpoll_release_file(). - * Returns true if the eventpoll can be disposed. - */ -static bool __ep_remove(struct eventpoll *ep, struct epitem *epi, bool force) -{ - struct file *file = epi->ffd.file; - - lockdep_assert_irqs_enabled(); - - /* - * Removes poll wait queue hooks. - */ - ep_unregister_pollwait(ep, epi); - - /* Remove the current item from the list of epoll hooks */ - spin_lock(&file->f_lock); - if (epi->dying && !force) { - spin_unlock(&file->f_lock); - return false; - } - - __ep_remove_file(ep, epi, file); - return __ep_remove_epi(ep, epi); -} - /* * Called with &file->f_lock held, * returns with it released */ -static void __ep_remove_file(struct eventpoll *ep, struct epitem *epi, struct file *file) +static void __ep_remove_file(struct eventpoll *ep, struct epitem *epi, + struct file *file) { struct epitems_head *to_free = NULL; struct hlist_head *head = file->f_ep; lockdep_assert_held(&ep->mtx); + lockdep_assert_held(&file->f_lock); if (hlist_is_singular_node(&epi->fllink, head)) { /* See eventpoll_release() for details. */
@@ -915,7 +884,25 @@ static bool __ep_remove_epi(struct eventpoll *ep, struct epitem *epi) */ static void ep_remove_safe(struct eventpoll *ep, struct epitem *epi) { - if (__ep_remove(ep, epi, false)) + struct file *file = epi->ffd.file; + + lockdep_assert_irqs_enabled(); + lockdep_assert_held(&ep->mtx); + + ep_unregister_pollwait(ep, epi); + + /* sync with eventpoll_release_file() */ + if (unlikely(READ_ONCE(epi->dying))) + return; + + spin_lock(&file->f_lock); + if (epi->dying) { + spin_unlock(&file->f_lock); + return; + } + __ep_remove_file(ep, epi, file); + + if (__ep_remove_epi(ep, epi)) WARN_ON_ONCE(ep_refcount_dec_and_test(ep)); }
@@ -1147,7 +1134,7 @@ void eventpoll_release_file(struct file *file) spin_lock(&file->f_lock); if (file->f_ep && file->f_ep->first) { epi = hlist_entry(file->f_ep->first, struct epitem, fllink); - epi->dying = true; + WRITE_ONCE(epi->dying, true); spin_unlock(&file->f_lock); /*
@@ -1156,7 +1143,13 @@ void eventpoll_release_file(struct file *file) */ ep = epi->ep; mutex_lock(&ep->mtx); - dispose = __ep_remove(ep, epi, true); + + ep_unregister_pollwait(ep, epi); + + spin_lock(&file->f_lock); + __ep_remove_file(ep, epi, file); + dispose = __ep_remove_epi(ep, epi); + mutex_unlock(&ep->mtx); if (dispose && ep_refcount_dec_and_test(ep))
--
2.53.0