[PATCH] eventpoll: propagate wakeup source creation failure in ep_modify()

From: Tianyi Chen

Date: Mon Oct 19 2026 - 05:43:49 EST


When EPOLL_CTL_MOD adds EPOLLWAKEUP, ep_modify() ignores the return
value of ep_create_wakeup_source(). If allocation fails, it returns
success with EPOLLWAKEUP set but no per-item wakeup source, so the
requested suspend protection is missing.

This can happen without fault injection on SELinux systems: creating
the wakeup source's sysfs nodes can require GFP_KERNEL_ACCOUNT
allocations for security.selinux xattrs, which can fail when the
caller's memory cgroup is over its limit.

Create the wakeup source before updating the event mask and data, and
return the error if creation fails. Leave any newly created per-epoll
source in place until ep_free(), as ep_insert() does on failure.

Fixes: 4d7e30d98939 ("epoll: Add a flag, EPOLLWAKEUP, to prevent suspend while epoll events are ready")
Assisted-by: LLM
Signed-off-by: Tianyi Chen <hi@xxxxxxxxx>
---
Tested on v7.3-rc2 in QEMU/KVM:
- fail-nth: MOD 256 trials, ADD 270 trials, and MOD with an existing
per-epoll source 120 trials; no contract violations.
- SELinux + memcg: 20/20 MOD calls returned ENOMEM with the old mask.
- tools/testing/selftests/filesystems/epoll/: 66/66 passed.

fs/eventpoll.c | 18 +++++++++++++-----
1 file changed, 13 insertions(+), 5 deletions(-)

diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index e0c4bf88a838..872396a0ad64 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -1982,11 +1982,22 @@ static int ep_modify(struct eventpoll *ep, struct epitem *epi,
{
int pwake = 0;
poll_table pt;
+ int error;

lockdep_assert_irqs_enabled();

init_poll_funcptr(&pt, NULL);

+ /*
+ * Create the wakeup source before committing the new event mask.
+ * Leave the old mask and data unchanged if allocation fails.
+ */
+ if ((event->events & EPOLLWAKEUP) && !ep_has_wakeup_source(epi)) {
+ error = ep_create_wakeup_source(epi);
+ if (error)
+ return error;
+ }
+
/*
* Set the new event interest mask before calling f_op->poll();
* otherwise we might miss an event that happens between the
@@ -1994,12 +2005,9 @@ static int ep_modify(struct eventpoll *ep, struct epitem *epi,
*/
epi->event.events = event->events; /* need barrier below */
epi->event.data = event->data; /* protected by mtx */
- if (epi->event.events & EPOLLWAKEUP) {
- if (!ep_has_wakeup_source(epi))
- ep_create_wakeup_source(epi);
- } else if (ep_has_wakeup_source(epi)) {
+ if (!(epi->event.events & EPOLLWAKEUP) &&
+ ep_has_wakeup_source(epi))
ep_destroy_wakeup_source(epi);
- }

/*
* The following barrier has two effects:

base-commit: df2908090cda368b01ff43709f51890076c56157
--
2.56.0