Fix CVE-2026-46242 (eventpoll ep_remove UAF); bump to 687.20.3
This commit is contained in:
parent
5ff608da79
commit
0a570b6523
@ -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 14e7663..9c56062 100644
|
||||
--- a/fs/eventpoll.c
|
||||
+++ b/fs/eventpoll.c
|
||||
@@ -711,6 +711,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.
|
||||
@@ -722,6 +724,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;
|
||||
|
||||
@@ -732,13 +735,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) {
|
||||
@@ -772,6 +789,8 @@ static bool __ep_remove(struct eventpoll *ep, struct epitem *epi, bool force)
|
||||
call_rcu(&epi->rcu, epi_rcu_free);
|
||||
|
||||
percpu_counter_dec(&ep->user->epoll_watches);
|
||||
+ if (to_put)
|
||||
+ fput(to_put);
|
||||
return true;
|
||||
}
|
||||
|
||||
--
|
||||
2.43.0
|
||||
|
||||
@ -176,13 +176,13 @@ Summary: The Linux kernel
|
||||
# define buildid .local
|
||||
%define specversion 5.14.0
|
||||
%define patchversion 5.14
|
||||
%define pkgrelease 687.20.1
|
||||
%define pkgrelease 687.20.3
|
||||
%define kversion 5
|
||||
%define tarfile_release 5.14.0-687.5.1.el9_8
|
||||
# This is needed to do merge window version magic
|
||||
%define patchlevel 14
|
||||
# This allows pkg_release to have configurable %%{?dist} tag
|
||||
%define specrelease 687.20.2%{?buildid}%{?dist}
|
||||
%define specrelease 687.20.3%{?buildid}%{?dist}
|
||||
# This defines the kabi tarball version
|
||||
%define kabiversion 5.14.0-687.5.1.el9_8
|
||||
|
||||
@ -1589,6 +1589,7 @@ Patch1708: 1708-crypto-krb5enc-fix-async-decrypt-skipping-hash-verification.patc
|
||||
Patch1709: 1709-crypto-krb5-filter-out-async-aead-implementations-at-alloc.patch
|
||||
Patch1710: 1710-kvm-x86-fix-shadow-paging-use-after-free-due-to-unexpected-gfn.patch
|
||||
Patch1711: 1711-kvm-x86-fix-shadow-paging-use-after-free-due-to-unexpected-role.patch
|
||||
Patch1712: 1712-eventpoll-fix-ep-remove-struct-eventpoll-struct-file-uaf.patch
|
||||
# END OF PATCH DEFINITIONS
|
||||
|
||||
%description
|
||||
@ -2945,6 +2946,7 @@ ApplyPatch 1708-crypto-krb5enc-fix-async-decrypt-skipping-hash-verification.patc
|
||||
ApplyPatch 1709-crypto-krb5-filter-out-async-aead-implementations-at-alloc.patch
|
||||
ApplyPatch 1710-kvm-x86-fix-shadow-paging-use-after-free-due-to-unexpected-gfn.patch
|
||||
ApplyPatch 1711-kvm-x86-fix-shadow-paging-use-after-free-due-to-unexpected-role.patch
|
||||
ApplyPatch 1712-eventpoll-fix-ep-remove-struct-eventpoll-struct-file-uaf.patch
|
||||
# END OF PATCH APPLICATIONS
|
||||
|
||||
# Any further pre-build tree manipulations happen here.
|
||||
@ -5019,6 +5021,10 @@ fi
|
||||
#
|
||||
#
|
||||
%changelog
|
||||
* Mon Jul 06 2026 Andrew Lukoshko <alukoshko@almalinux.org> - 5.14.0-687.20.3
|
||||
- Fix CVE-2026-46242: eventpoll ep_remove struct eventpoll / struct file
|
||||
use-after-free, adapted from upstream a6dc643c6931 ahead of RHEL (1712)
|
||||
|
||||
* Thu Jul 02 2026 Andrew Lukoshko <alukoshko@almalinux.org> - 5.14.0-687.20.2
|
||||
- Fix KVM x86 shadow paging use-after-free due to unexpected GFN ahead of RHEL (1710)
|
||||
- Fix KVM x86 shadow paging use-after-free due to unexpected role ahead of RHEL (1711)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user