Skip to content

Commit e9e5cd4

Browse files
committed
eventpoll: kill __ep_remove()
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>
1 parent 0f7bdfd commit e9e5cd4

1 file changed

Lines changed: 30 additions & 37 deletions

File tree

fs/eventpoll.c

Lines changed: 30 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -826,49 +826,18 @@ static void ep_free(struct eventpoll *ep)
826826
kfree_rcu(ep, rcu);
827827
}
828828

829-
static void __ep_remove_file(struct eventpoll *ep, struct epitem *epi, struct file *file);
830-
static bool __ep_remove_epi(struct eventpoll *ep, struct epitem *epi);
831-
832-
/*
833-
* Removes a "struct epitem" from the eventpoll RB tree and deallocates
834-
* all the associated resources. Must be called with "mtx" held.
835-
* If the dying flag is set, do the removal only if force is true.
836-
* This prevents ep_clear_and_put() from dropping all the ep references
837-
* while running concurrently with eventpoll_release_file().
838-
* Returns true if the eventpoll can be disposed.
839-
*/
840-
static bool __ep_remove(struct eventpoll *ep, struct epitem *epi, bool force)
841-
{
842-
struct file *file = epi->ffd.file;
843-
844-
lockdep_assert_irqs_enabled();
845-
846-
/*
847-
* Removes poll wait queue hooks.
848-
*/
849-
ep_unregister_pollwait(ep, epi);
850-
851-
/* Remove the current item from the list of epoll hooks */
852-
spin_lock(&file->f_lock);
853-
if (epi->dying && !force) {
854-
spin_unlock(&file->f_lock);
855-
return false;
856-
}
857-
858-
__ep_remove_file(ep, epi, file);
859-
return __ep_remove_epi(ep, epi);
860-
}
861-
862829
/*
863830
* Called with &file->f_lock held,
864831
* returns with it released
865832
*/
866-
static void __ep_remove_file(struct eventpoll *ep, struct epitem *epi, struct file *file)
833+
static void __ep_remove_file(struct eventpoll *ep, struct epitem *epi,
834+
struct file *file)
867835
{
868836
struct epitems_head *to_free = NULL;
869837
struct hlist_head *head = file->f_ep;
870838

871839
lockdep_assert_held(&ep->mtx);
840+
lockdep_assert_held(&file->f_lock);
872841

873842
if (hlist_is_singular_node(&epi->fllink, head)) {
874843
/* See eventpoll_release() for details. */
@@ -915,7 +884,25 @@ static bool __ep_remove_epi(struct eventpoll *ep, struct epitem *epi)
915884
*/
916885
static void ep_remove_safe(struct eventpoll *ep, struct epitem *epi)
917886
{
918-
if (__ep_remove(ep, epi, false))
887+
struct file *file = epi->ffd.file;
888+
889+
lockdep_assert_irqs_enabled();
890+
lockdep_assert_held(&ep->mtx);
891+
892+
ep_unregister_pollwait(ep, epi);
893+
894+
/* sync with eventpoll_release_file() */
895+
if (unlikely(READ_ONCE(epi->dying)))
896+
return;
897+
898+
spin_lock(&file->f_lock);
899+
if (epi->dying) {
900+
spin_unlock(&file->f_lock);
901+
return;
902+
}
903+
__ep_remove_file(ep, epi, file);
904+
905+
if (__ep_remove_epi(ep, epi))
919906
WARN_ON_ONCE(ep_refcount_dec_and_test(ep));
920907
}
921908

@@ -1147,7 +1134,7 @@ void eventpoll_release_file(struct file *file)
11471134
spin_lock(&file->f_lock);
11481135
if (file->f_ep && file->f_ep->first) {
11491136
epi = hlist_entry(file->f_ep->first, struct epitem, fllink);
1150-
epi->dying = true;
1137+
WRITE_ONCE(epi->dying, true);
11511138
spin_unlock(&file->f_lock);
11521139

11531140
/*
@@ -1156,7 +1143,13 @@ void eventpoll_release_file(struct file *file)
11561143
*/
11571144
ep = epi->ep;
11581145
mutex_lock(&ep->mtx);
1159-
dispose = __ep_remove(ep, epi, true);
1146+
1147+
ep_unregister_pollwait(ep, epi);
1148+
1149+
spin_lock(&file->f_lock);
1150+
__ep_remove_file(ep, epi, file);
1151+
dispose = __ep_remove_epi(ep, epi);
1152+
11601153
mutex_unlock(&ep->mtx);
11611154

11621155
if (dispose && ep_refcount_dec_and_test(ep))

0 commit comments

Comments
 (0)