Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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>

+30 -37
+30 -37
fs/eventpoll.c
··· 826 826 kfree_rcu(ep, rcu); 827 827 } 828 828 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 - 862 829 /* 863 830 * Called with &file->f_lock held, 864 831 * returns with it released 865 832 */ 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) 867 835 { 868 836 struct epitems_head *to_free = NULL; 869 837 struct hlist_head *head = file->f_ep; 870 838 871 839 lockdep_assert_held(&ep->mtx); 840 + lockdep_assert_held(&file->f_lock); 872 841 873 842 if (hlist_is_singular_node(&epi->fllink, head)) { 874 843 /* See eventpoll_release() for details. */ ··· 884 915 */ 885 916 static void ep_remove_safe(struct eventpoll *ep, struct epitem *epi) 886 917 { 887 - if (__ep_remove(ep, epi, false)) 918 + struct file *file = epi->ffd.file; 919 + 920 + lockdep_assert_irqs_enabled(); 921 + lockdep_assert_held(&ep->mtx); 922 + 923 + ep_unregister_pollwait(ep, epi); 924 + 925 + /* sync with eventpoll_release_file() */ 926 + if (unlikely(READ_ONCE(epi->dying))) 927 + return; 928 + 929 + spin_lock(&file->f_lock); 930 + if (epi->dying) { 931 + spin_unlock(&file->f_lock); 932 + return; 933 + } 934 + __ep_remove_file(ep, epi, file); 935 + 936 + if (__ep_remove_epi(ep, epi)) 888 937 WARN_ON_ONCE(ep_refcount_dec_and_test(ep)); 889 938 } 890 939 ··· 1134 1147 spin_lock(&file->f_lock); 1135 1148 if (file->f_ep && file->f_ep->first) { 1136 1149 epi = hlist_entry(file->f_ep->first, struct epitem, fllink); 1137 - epi->dying = true; 1150 + WRITE_ONCE(epi->dying, true); 1138 1151 spin_unlock(&file->f_lock); 1139 1152 1140 1153 /* ··· 1143 1156 */ 1144 1157 ep = epi->ep; 1145 1158 mutex_lock(&ep->mtx); 1146 - dispose = __ep_remove(ep, epi, true); 1159 + 1160 + ep_unregister_pollwait(ep, epi); 1161 + 1162 + spin_lock(&file->f_lock); 1163 + __ep_remove_file(ep, epi, file); 1164 + dispose = __ep_remove_epi(ep, epi); 1165 + 1147 1166 mutex_unlock(&ep->mtx); 1148 1167 1149 1168 if (dispose && ep_refcount_dec_and_test(ep))