From 3df1ed4e4bd1b9b514e13198231829474bccdacc Mon Sep 17 00:00:00 2001 From: Andrew Lukoshko Date: Mon, 6 Jul 2026 12:55:39 +0000 Subject: [PATCH] Fix CVE-2026-46242 (eventpoll ep_remove UAF); bump to 211.30.3 --- ...ove-struct-eventpoll-struct-file-uaf.patch | 99 +++++++++++++++++++ kernel.spec | 10 +- 2 files changed, 107 insertions(+), 2 deletions(-) create mode 100644 1417-eventpoll-fix-ep-remove-struct-eventpoll-struct-file-uaf.patch diff --git a/1417-eventpoll-fix-ep-remove-struct-eventpoll-struct-file-uaf.patch b/1417-eventpoll-fix-ep-remove-struct-eventpoll-struct-file-uaf.patch new file mode 100644 index 000000000..223e93645 --- /dev/null +++ b/1417-eventpoll-fix-ep-remove-struct-eventpoll-struct-file-uaf.patch @@ -0,0 +1,99 @@ +From: Christian Brauner +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 +Signed-off-by: Christian Brauner +--- +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 + diff --git a/kernel.spec b/kernel.spec index 0aaa0efbc..501ad2142 100644 --- a/kernel.spec +++ b/kernel.spec @@ -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 - 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 - 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)