[PATCH 6.12.y 0/7] eventpoll: backport a6dc643c69311677c574a0f17a3f4d66a5f3744b

From: Quentin Schulz

Date: Fri Jun 19 2026 - 11:03:52 EST


Backport a6dc643c6931 ("eventpoll: fix ep_remove struct eventpoll /
struct file UAF") to 6.12.y. So the patch applies cleanly, commit
86e87059e6d1 ("eventpoll: move epi_fget() up"), commit 0bade234723e
("eventpoll: rename ep_remove_safe() back to ep_remove()"), commit
0feaf644f718 ("eventpoll: drop vestigial __ prefix from
ep_remove_{file,epi}()"), commit e9e5cd40d7c4 ("eventpoll: kill
__ep_remove()"), commit 0f7bdfd41300 ("eventpoll: split __ep_remove()")
and commit 3d9fd0abc94d ("eventpoll: use hlist_is_singular_node() in
__ep_remove()") are also backported.

Note that backport of commit 86e87059e6d1 ("eventpoll: move epi_fget()
up") conflicted due to missing commit 90ee6ed776c0 ("fs: port files to
file_ref") and its dependent commit 08ef26ea9ab3 ("fs: add file_ref").
The original commit is simply moving a function earlier in the file, so
we do the same even if the content of the function is actually slightly
different. I opted for this instead of backporting the other two commits
because they look a bit more involved than I would like to for stable.
They also do not apply cleanly so I drew the line before those two
"dependencies" and didn't add them to the list of backported patches in
this series.

Note that backport of 0bade234723e ("eventpoll: rename ep_remove_safe()
back to ep_remove()") is not necessary (e.g. 6.18.y doesn't have it), it
just makes git-range-diff even smaller so I thought it was nice to add
it. Maybe it'll make future backports easier too /me shrugs.

The changes between 3d9fd0abc94d^..a6dc643c6931 (commit log excluded)
and this series is (according to git-range-diff):

"""
## fs/eventpoll.c ##
@@ fs/eventpoll.c: static void ep_free(struct eventpoll *ep)
@@ fs/eventpoll.c: static void ep_free(struct eventpoll *ep)
+ struct file *file;
+
+ file = epi->ffd.file;
-+ if (!file_ref_get(&file->f_ref))
++ if (!atomic_long_inc_not_zero(&file->f_count))
+ file = NULL;
+ return file;
+}
@@ fs/eventpoll.c: static __poll_t __ep_eventpoll_poll(struct file *file, poll_tabl
- struct file *file;
-
- file = epi->ffd.file;
-- if (!file_ref_get(&file->f_ref))
+- if (!atomic_long_inc_not_zero(&file->f_count))
- file = NULL;
- return file;
-}
"""

in patch 6.

Note that this series cleanly applies to v6.6.y as well but fails to
build with the following error:

/home/qschulz/work/upstream/linux/fs/eventpoll.c: In function ‘ep_remove’:
/home/qschulz/work/upstream/linux/fs/eventpoll.c:804:16: error: cleanup argument not a function
804 | struct file *file __free(fput) = NULL;
| ^~~~
make[4]: *** [/home/qschulz/work/upstream/linux/scripts/Makefile.build:243: fs/eventpoll.o] Error 1
make[4]: *** Waiting for unfinished jobs....

hence why I made this series 6.12.y-specific.

Signed-off-by: Quentin Schulz <quentin.schulz@xxxxxxxxx>
---
Christian Brauner (7):
eventpoll: use hlist_is_singular_node() in __ep_remove()
eventpoll: split __ep_remove()
eventpoll: kill __ep_remove()
eventpoll: drop vestigial __ prefix from ep_remove_{file,epi}()
eventpoll: rename ep_remove_safe() back to ep_remove()
eventpoll: move epi_fget() up
eventpoll: fix ep_remove struct eventpoll / struct file UAF

fs/eventpoll.c | 142 ++++++++++++++++++++++++++++++++-------------------------
1 file changed, 79 insertions(+), 63 deletions(-)
---
base-commit: 0b8f247169e487eff2d4c2dd531bc43f7efda2cb
change-id: 20260619-6-12-cve-2026-46242-b3ceffc753a1

Best regards,
--
Quentin Schulz <quentin.schulz@xxxxxxxxx>