Fix CVE-2026-46242 (eventpoll ep_remove UAF); bump to 211.30.3

This commit is contained in:
Andrew Lukoshko 2026-07-06 12:55:39 +00:00
parent 9d9b973c72
commit 3df1ed4e4b
2 changed files with 107 additions and 2 deletions

View File

@ -0,0 +1,99 @@
From: Christian Brauner <brauner@kernel.org>
Date: Thu, 23 Apr 2026 11:56:09 +0200
Subject: [PATCH] eventpoll: fix ep_remove struct eventpoll / struct file UAF
CVE: CVE-2026-46242
Upstream commit a6dc643c69311677c574a0f17a3f4d66a5f3744b, adapted to the
pre-e9e5cd40d7c4 ("eventpoll: kill __ep_remove()") code shape still used
by this kernel, where the removal path is the monolithic __ep_remove(ep,
epi, force) rather than upstream's ep_remove()/ep_remove_epi()/
ep_remove_file() split.
ep_remove() used epi->ffd.file across the file->f_lock critical section
without holding a reference. A concurrent __fput() taking the
eventpoll_release() fastpath could free the file (and, for the
epoll-watches-epoll case, the watched struct eventpoll) mid-section,
leading to a use-after-free / attacker-controllable kmem_cache_free().
Pin the file with epi_fget() before entering the critical section and
release it afterwards. A failed pin means __fput() is already in flight
and will remove the epi via eventpoll_release_file() (which blocks on
ep->mtx), so it is safe to bail. The pin is taken only on the !force
path: the force path is reached from eventpoll_release_file() itself,
i.e. from within __fput() where f_count has already dropped to zero and
epi_fget() would necessarily fail; there @file is still valid and no
concurrent __fput() can race, so it keeps using epi->ffd.file directly.
A successful pin also makes the in-lock epi->dying recheck redundant; the
cheap lockless READ_ONCE(epi->dying) fast-path bailout is kept.
Fixes: 58c9b016e128 ("epoll: use refcount to reduce ep_mutex contention")
Reported-by: Jaeyoung Chung <jjy600901@snu.ac.kr>
Signed-off-by: Christian Brauner <brauner@kernel.org>
---
diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index f4efb5b..68e58d0 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -821,6 +821,8 @@ static void ep_free(struct eventpoll *ep)
kfree(ep);
}
+static struct file *epi_fget(const struct epitem *epi);
+
/*
* Removes a "struct epitem" from the eventpoll RB tree and deallocates
* all the associated resources. Must be called with "mtx" held.
@@ -832,6 +834,7 @@ static void ep_free(struct eventpoll *ep)
static bool __ep_remove(struct eventpoll *ep, struct epitem *epi, bool force)
{
struct file *file = epi->ffd.file;
+ struct file *to_put = NULL;
struct epitems_head *to_free;
struct hlist_head *head;
@@ -842,13 +845,27 @@ static bool __ep_remove(struct eventpoll *ep, struct epitem *epi, bool force)
*/
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;
+ if (!force) {
+ /* cheap sync with eventpoll_release_file() */
+ if (unlikely(READ_ONCE(epi->dying)))
+ return false;
+ /*
+ * Grabbing a reference proves we are not racing
+ * eventpoll_release_file() and are not going to: a concurrent
+ * __fput() cannot free @file (nor, for the epoll-on-epoll
+ * case, the watched eventpoll) across the f_lock section
+ * below. A failed pin means @file already reached refcount
+ * zero and __fput() is in flight; it will remove this epi via
+ * eventpoll_release_file(), so bail out here.
+ */
+ file = epi_fget(epi);
+ if (!file)
+ return false;
+ to_put = file;
}
+ /* Remove the current item from the list of epoll hooks */
+ spin_lock(&file->f_lock);
to_free = NULL;
head = file->f_ep;
if (head->first == &epi->fllink && !epi->fllink.next) {
@@ -882,6 +899,8 @@ static bool __ep_remove(struct eventpoll *ep, struct epitem *epi, bool force)
kfree_rcu(epi, rcu);
percpu_counter_dec(&ep->user->epoll_watches);
+ if (to_put)
+ fput(to_put);
return true;
}
--
2.43.0

View File

@ -176,13 +176,13 @@ Summary: The Linux kernel
%define specrpmversion 6.12.0
%define specversion 6.12.0
%define patchversion 6.12
%define pkgrelease 211.30.1
%define pkgrelease 211.30.3
%define kversion 6
%define tarfile_release 6.12.0-211.7.1.el10_2
# This is needed to do merge window version magic
%define patchlevel 12
# This allows pkg_release to have configurable %%{?dist} tag
%define specrelease 211.30.2%{?buildid}%{?dist}
%define specrelease 211.30.3%{?buildid}%{?dist}
# This defines the kabi tarball version
%define kabiversion 6.12.0-211.7.1.el10_2
@ -1451,6 +1451,7 @@ Patch1413: 1413-arm64-errata-mitigate-tlbi-errata-on-microsoft-azure-cobalt.patc
Patch1414: 1414-fs-smb-client-fix-out-of-bounds-read-in-cifs-sanitize-prepa.patch
Patch1415: 1415-kvm-x86-fix-shadow-paging-use-after-free-due-to-unexpected-gfn.patch
Patch1416: 1416-kvm-x86-fix-shadow-paging-use-after-free-due-to-unexpected-role.patch
Patch1417: 1417-eventpoll-fix-ep-remove-struct-eventpoll-struct-file-uaf.patch
# END OF PATCH DEFINITIONS
%description
@ -2624,6 +2625,7 @@ ApplyPatch 1413-arm64-errata-mitigate-tlbi-errata-on-microsoft-azure-cobalt.patc
ApplyPatch 1414-fs-smb-client-fix-out-of-bounds-read-in-cifs-sanitize-prepa.patch
ApplyPatch 1415-kvm-x86-fix-shadow-paging-use-after-free-due-to-unexpected-gfn.patch
ApplyPatch 1416-kvm-x86-fix-shadow-paging-use-after-free-due-to-unexpected-role.patch
ApplyPatch 1417-eventpoll-fix-ep-remove-struct-eventpoll-struct-file-uaf.patch
# END OF PATCH APPLICATIONS
# Any further pre-build tree manipulations happen here.
@ -5128,6 +5130,10 @@ fi\
#
#
%changelog
* Mon Jul 06 2026 Andrew Lukoshko <alukoshko@almalinux.org> - 6.12.0-211.30.3
- Fix CVE-2026-46242: eventpoll ep_remove struct eventpoll / struct file
use-after-free, adapted from upstream a6dc643c6931 ahead of RHEL (1417)
* Fri Jul 03 2026 Andrew Lukoshko <alukoshko@almalinux.org> - 6.12.0-211.30.2
- Fix KVM x86 shadow paging use-after-free due to unexpected GFN ahead of RHEL (1415)
- Fix KVM x86 shadow paging use-after-free due to unexpected role ahead of RHEL (1416)