Recreate RHEL 6.12.0-211.31.1 from CS10/upstream backports

- Drop AlmaLinux ahead-of-RHEL eventpoll CVE-2026-46242 fix (1417), superseded
  by the RHEL eventpoll series in 211.31.1
- Temporarily drop the rtmutex remove_waiter() fixes (1418, 1420)
- Add RHEL 211.31.1 backports recreated from CS10/upstream (1421-1444)
This commit is contained in:
Andrew Lukoshko 2026-07-08 15:15:41 +00:00
parent 4f6054905f
commit 5edaaf4d17
28 changed files with 1985 additions and 263 deletions

View File

@ -1,99 +0,0 @@
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

@ -1,86 +0,0 @@
From 3bfdc63936dd4773109b7b8c280c0f3b5ae7d349 Mon Sep 17 00:00:00 2001
From: Keenan Dong <keenanat2000@gmail.com>
Date: Wed, 8 Apr 2026 16:46:00 +0800
Subject: [PATCH] rtmutex: Use waiter::task instead of current in
remove_waiter()
remove_waiter() is used by the slowlock paths, but it is also used for
proxy-lock rollback in rt_mutex_start_proxy_lock() when invoked from
futex_requeue().
In the latter case waiter::task is not current, but remove_waiter()
operates on current for the dequeue operation. That results in several
problems:
1) the rbtree dequeue happens without waiter::task::pi_lock being held
2) the waiter task's pi_blocked_on state is not cleared, which leaves a
dangling pointer primed for UAF around.
3) rt_mutex_adjust_prio_chain() operates on the wrong top priority waiter
task
Use waiter::task instead of current in all related operations in
remove_waiter() to cure those problems.
[ tglx: Fixup rt_mutex_adjust_prio_chain(), add a comment and amend the
changelog ]
Fixes: 8161239a8bcc ("rtmutex: Simplify PI algorithm and make highest prio task get lock")
Reported-by: Yuan Tan <yuantan098@gmail.com>
Reported-by: Yifan Wu <yifanwucs@gmail.com>
Reported-by: Juefei Pu <tomapufckgml@gmail.com>
Reported-by: Xin Liu <bird@lzu.edu.cn>
Signed-off-by: Keenan Dong <keenanat2000@gmail.com>
Signed-off-by: Thomas Gleixner <tglx@kernel.org>
Cc: stable@vger.kernel.org
(cherry picked from commit 3bfdc63936dd4773109b7b8c280c0f3b5ae7d349)
[AlmaLinux: adapted to a10 (6.12); context/line offsets only. scoped_guard(raw_spinlock, ...) is available in this tree (include/linux/cleanup.h + DEFINE_LOCK_GUARD_1 raw_spinlock in include/linux/spinlock.h), so the upstream form is used verbatim.]
---
kernel/locking/rtmutex.c | 13 ++++++++-----
1 file changed, 8 insertions(+), 5 deletions(-)
diff --git a/kernel/locking/rtmutex.c b/kernel/locking/rtmutex.c
index 4a8df18..e6f305d 100644
--- a/kernel/locking/rtmutex.c
+++ b/kernel/locking/rtmutex.c
@@ -1534,20 +1534,23 @@ static bool rtmutex_spin_on_owner(struct rt_mutex_base *lock,
*
* Must be called with lock->wait_lock held and interrupts disabled. It must
* have just failed to try_to_take_rt_mutex().
+ *
+ * When invoked from rt_mutex_start_proxy_lock() waiter::task != current !
*/
static void __sched remove_waiter(struct rt_mutex_base *lock,
struct rt_mutex_waiter *waiter)
{
bool is_top_waiter = (waiter == rt_mutex_top_waiter(lock));
struct task_struct *owner = rt_mutex_owner(lock);
+ struct task_struct *waiter_task = waiter->task;
struct rt_mutex_base *next_lock;
lockdep_assert_held(&lock->wait_lock);
- raw_spin_lock(&current->pi_lock);
- rt_mutex_dequeue(lock, waiter);
- current->pi_blocked_on = NULL;
- raw_spin_unlock(&current->pi_lock);
+ scoped_guard(raw_spinlock, &waiter_task->pi_lock) {
+ rt_mutex_dequeue(lock, waiter);
+ waiter_task->pi_blocked_on = NULL;
+ }
/*
* Only update priority if the waiter was the highest priority
@@ -1583,7 +1586,7 @@ static void __sched remove_waiter(struct rt_mutex_base *lock,
raw_spin_unlock_irq(&lock->wait_lock);
rt_mutex_adjust_prio_chain(owner, RT_MUTEX_MIN_CHAINWALK, lock,
- next_lock, NULL, current);
+ next_lock, NULL, waiter_task);
raw_spin_lock_irq(&lock->wait_lock);
}
--
2.50.1

View File

@ -1,70 +0,0 @@
From 40a25d59e85b3c8709ac2424d44f65610467871e Mon Sep 17 00:00:00 2001
From: Davidlohr Bueso <dave@stgolabs.net>
Date: Thu, 7 May 2026 04:29:13 -0700
Subject: [PATCH] locking/rtmutex: Skip remove_waiter() when waiter is not
enqueued
syzbot triggered the following splat in remove_waiter() via
FUTEX_CMP_REQUEUE_PI:
KASAN: null-ptr-deref in range [0x0000000000000a88-0x0000000000000a8f]
class_raw_spinlock_constructor
remove_waiter+0x159/0x1200 kernel/locking/rtmutex.c:1561
rt_mutex_start_proxy_lock+0x103/0x120
futex_requeue+0x10e4/0x20d0
__x64_sys_futex+0x34f/0x4d0
task_blocks_on_rt_mutex() does not arm the waiter upon deadlock detection,
leaving waiter->task nil, where 3bfdc63936dd ("rtmutex: Use waiter::task instead
of current in remove_waiter()") made this fatal.
Furthermore, rt_mutex_start_proxy_lock() should not be calling into remove_waiter()
upon a successfully grabbing the rtmutex. 1a1fb985f2e2 ("futex: Handle early deadlock
return correctly"), moved the remove_waiter() out of __rt_mutex_start_proxy_lock()
(where 'ret' was only ever 0 or < 0) into the wrapper. Tighten this check to
account for try_to_take_rt_mutex().
Fixes: 3bfdc63936dd ("rtmutex: Use waiter::task instead of current in remove_waiter()")
Reported-by: syzbot+78147abe6c524f183ee9@syzkaller.appspotmail.com
Signed-off-by: Davidlohr Bueso <dave@stgolabs.net>
Signed-off-by: Thomas Gleixner <tglx@kernel.org>
Cc: stable@vger.kernel.org
Closes: https://lore.kernel.org/all/69f114ac.050a0220.ac8b.0003.GAE@google.com/
Link: https://patch.msgid.link/20260507112913.1019537-1-dave@stgolabs.net
(cherry picked from commit 40a25d59e85b3c8709ac2424d44f65610467871e)
[AlmaLinux: adapted to a10 (6.12); context/line offsets only. Both hunks apply: remove_waiter() in kernel/locking/rtmutex.c (depends on waiter_task local from 3bfdc63936dd) and rt_mutex_start_proxy_lock() in kernel/locking/rtmutex_api.c. In this tree __rt_mutex_start_proxy_lock() returns 1 on try_to_take_rt_mutex() success, so the ret<0 tightening is required to avoid calling remove_waiter() on lock acquisition.]
---
kernel/locking/rtmutex.c | 3 +++
kernel/locking/rtmutex_api.c | 2 +-
2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/kernel/locking/rtmutex.c b/kernel/locking/rtmutex.c
index e6f305d..15111dc 100644
--- a/kernel/locking/rtmutex.c
+++ b/kernel/locking/rtmutex.c
@@ -1547,6 +1547,9 @@ static void __sched remove_waiter(struct rt_mutex_base *lock,
lockdep_assert_held(&lock->wait_lock);
+ if (!waiter_task) /* never enqueued */
+ return;
+
scoped_guard(raw_spinlock, &waiter_task->pi_lock) {
rt_mutex_dequeue(lock, waiter);
waiter_task->pi_blocked_on = NULL;
diff --git a/kernel/locking/rtmutex_api.c b/kernel/locking/rtmutex_api.c
index 6614ccd..341a540 100644
--- a/kernel/locking/rtmutex_api.c
+++ b/kernel/locking/rtmutex_api.c
@@ -347,7 +347,7 @@ int __sched rt_mutex_start_proxy_lock(struct rt_mutex_base *lock,
raw_spin_lock_irq(&lock->wait_lock);
ret = __rt_mutex_start_proxy_lock(lock, waiter, task, &wake_q);
- if (unlikely(ret))
+ if (unlikely(ret < 0))
remove_waiter(lock, waiter);
preempt_disable();
raw_spin_unlock_irq(&lock->wait_lock);
--
2.50.1

View File

@ -0,0 +1,71 @@
From 942ef6b142b4e78e63c115e806f67ab31b8106d6 Mon Sep 17 00:00:00 2001
From: CKI Backport Bot <cki-ci-bot+cki-gitlab-backport-bot@redhat.com>
Date: Mon, 30 Mar 2026 08:07:00 +0000
Subject: [PATCH] net: page_pool: avoid false positive warning if NAPI was
never added
JIRA: https://redhat.atlassian.net/browse/RHEL-162141
commit c1e00bc4be06cacee6307cedb9b55bbaddb5044d
Author: Jakub Kicinski <kuba@kernel.org>
Date: Thu Feb 6 14:56:37 2025 -0800
net: page_pool: avoid false positive warning if NAPI was never added
We expect NAPI to be in disabled state when page pool is torn down.
But it is also legal if the NAPI is completely uninitialized.
Reviewed-by: Mina Almasry <almasrymina@google.com>
Link: https://patch.msgid.link/20250206225638.1387810-4-kuba@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: CKI Backport Bot <cki-ci-bot+cki-gitlab-backport-bot@redhat.com>
diff --git a/net/core/dev.h b/net/core/dev.h
index ddf018a0d..f4fc420f7 100644
--- a/net/core/dev.h
+++ b/net/core/dev.h
@@ -347,6 +347,18 @@ void xdp_do_check_flushed(struct napi_struct *napi);
static inline void xdp_do_check_flushed(struct napi_struct *napi) { }
#endif
+/* Best effort check that NAPI is not idle (can't be scheduled to run) */
+static inline void napi_assert_will_not_race(const struct napi_struct *napi)
+{
+ /* uninitialized instance, can't race */
+ if (!napi->poll_list.next)
+ return;
+
+ /* SCHED bit is set on disabled instances */
+ WARN_ON(!test_bit(NAPI_STATE_SCHED, &napi->state));
+ WARN_ON(READ_ONCE(napi->list_owner) != -1);
+}
+
void kick_defer_list_purge(struct softnet_data *sd, unsigned int cpu);
#define XMIT_RECURSION_LIMIT 8
diff --git a/net/core/page_pool.c b/net/core/page_pool.c
index 06531b7b7..15b08364d 100644
--- a/net/core/page_pool.c
+++ b/net/core/page_pool.c
@@ -26,6 +26,7 @@
#include <trace/events/page_pool.h>
+#include RH_KABI_HIDE_INCLUDE("dev.h")
#include "mp_dmabuf_devmem.h"
#include "netmem_priv.h"
#include "page_pool_priv.h"
@@ -1196,11 +1197,7 @@ void page_pool_disable_direct_recycling(struct page_pool *pool)
if (!pool->p.napi)
return;
- /* To avoid races with recycling and additional barriers make sure
- * pool and NAPI are unlinked when NAPI is disabled.
- */
- WARN_ON(!test_bit(NAPI_STATE_SCHED, &pool->p.napi->state));
- WARN_ON(READ_ONCE(pool->p.napi->list_owner) != -1);
+ napi_assert_will_not_race(pool->p.napi);
mutex_lock(&page_pools_lock);
WRITE_ONCE(pool->p.napi, NULL);

View File

@ -0,0 +1,82 @@
From f975a0955276579e2176a134366ed586071c7c6a Mon Sep 17 00:00:00 2001
From: Dipayaan Roy <dipayanroy@linux.microsoft.com>
Date: Tue, 24 Feb 2026 04:38:36 -0800
Subject: [PATCH] net: mana: Fix double destroy_workqueue on service rescan PCI
path
While testing corner cases in the driver, a use-after-free crash
was found on the service rescan PCI path.
When mana_serv_reset() calls mana_gd_suspend(), mana_gd_cleanup()
destroys gc->service_wq. If the subsequent mana_gd_resume() fails
with -ETIMEDOUT or -EPROTO, the code falls through to
mana_serv_rescan() which triggers pci_stop_and_remove_bus_device().
This invokes the PCI .remove callback (mana_gd_remove), which calls
mana_gd_cleanup() a second time, attempting to destroy the already-
freed workqueue. Fix this by NULL-checking gc->service_wq in
mana_gd_cleanup() and setting it to NULL after destruction.
Call stack of issue for reference:
[Sat Feb 21 18:53:48 2026] Call Trace:
[Sat Feb 21 18:53:48 2026] <TASK>
[Sat Feb 21 18:53:48 2026] mana_gd_cleanup+0x33/0x70 [mana]
[Sat Feb 21 18:53:48 2026] mana_gd_remove+0x3a/0xc0 [mana]
[Sat Feb 21 18:53:48 2026] pci_device_remove+0x41/0xb0
[Sat Feb 21 18:53:48 2026] device_remove+0x46/0x70
[Sat Feb 21 18:53:48 2026] device_release_driver_internal+0x1e3/0x250
[Sat Feb 21 18:53:48 2026] device_release_driver+0x12/0x20
[Sat Feb 21 18:53:48 2026] pci_stop_bus_device+0x6a/0x90
[Sat Feb 21 18:53:48 2026] pci_stop_and_remove_bus_device+0x13/0x30
[Sat Feb 21 18:53:48 2026] mana_do_service+0x180/0x290 [mana]
[Sat Feb 21 18:53:48 2026] mana_serv_func+0x24/0x50 [mana]
[Sat Feb 21 18:53:48 2026] process_one_work+0x190/0x3d0
[Sat Feb 21 18:53:48 2026] worker_thread+0x16e/0x2e0
[Sat Feb 21 18:53:48 2026] kthread+0xf7/0x130
[Sat Feb 21 18:53:48 2026] ? __pfx_worker_thread+0x10/0x10
[Sat Feb 21 18:53:48 2026] ? __pfx_kthread+0x10/0x10
[Sat Feb 21 18:53:48 2026] ret_from_fork+0x269/0x350
[Sat Feb 21 18:53:48 2026] ? __pfx_kthread+0x10/0x10
[Sat Feb 21 18:53:48 2026] ret_from_fork_asm+0x1a/0x30
[Sat Feb 21 18:53:48 2026] </TASK>
Fixes: 505cc26bcae0 ("net: mana: Add support for auxiliary device servicing events")
Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com>
Signed-off-by: Dipayaan Roy <dipayanroy@linux.microsoft.com>
Reviewed-by: Simon Horman <horms@kernel.org>
Link: https://patch.msgid.link/aZ2bzL64NagfyHpg@linuxonhyperv3.guj3yctzbm1etfxqx2vob5hsef.xx.internal.cloudapp.net
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
diff --git a/drivers/net/ethernet/microsoft/mana/gdma_main.c b/drivers/net/ethernet/microsoft/mana/gdma_main.c
index 0055c231acf6..3926d18f1840 100644
--- a/drivers/net/ethernet/microsoft/mana/gdma_main.c
+++ b/drivers/net/ethernet/microsoft/mana/gdma_main.c
@@ -1946,7 +1946,10 @@ static void mana_gd_cleanup(struct pci_dev *pdev)
mana_gd_remove_irqs(pdev);
- destroy_workqueue(gc->service_wq);
+ if (gc->service_wq) {
+ destroy_workqueue(gc->service_wq);
+ gc->service_wq = NULL;
+ }
dev_dbg(&pdev->dev, "mana gdma cleanup successful\n");
}
diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c
index 9b5a72ada5c4..f69e42651359 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_en.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_en.c
@@ -3762,7 +3762,9 @@ void mana_rdma_remove(struct gdma_dev *gd)
}
WRITE_ONCE(gd->rdma_teardown, true);
- flush_workqueue(gc->service_wq);
+
+ if (gc->service_wq)
+ flush_workqueue(gc->service_wq);
if (gd->adev)
remove_adev(gd);
--
2.50.1 (Apple Git-155)

View File

@ -0,0 +1,33 @@
From 87c2302813abc55c46485711a678e3c312b00666 Mon Sep 17 00:00:00 2001
From: Shiraz Saleem <shirazsaleem@microsoft.com>
Date: Mon, 9 Mar 2026 10:24:43 -0700
Subject: [PATCH] net/mana: Null service_wq on setup error to prevent double
destroy
In mana_gd_setup() error path, set gc->service_wq to NULL after
destroy_workqueue() to match the cleanup in mana_gd_cleanup().
This prevents a use-after-free if the workqueue pointer is checked
after a failed setup.
Fixes: f975a0955276 ("net: mana: Fix double destroy_workqueue on service rescan PCI path")
Signed-off-by: Shiraz Saleem <shirazsaleem@microsoft.com>
Signed-off-by: Konstantin Taranov <kotaranov@microsoft.com>
Reviewed-by: Simon Horman <horms@kernel.org>
Link: https://patch.msgid.link/20260309172443.688392-1-kotaranov@linux.microsoft.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
diff --git a/drivers/net/ethernet/microsoft/mana/gdma_main.c b/drivers/net/ethernet/microsoft/mana/gdma_main.c
index 37d2f108a839..786186c9a115 100644
--- a/drivers/net/ethernet/microsoft/mana/gdma_main.c
+++ b/drivers/net/ethernet/microsoft/mana/gdma_main.c
@@ -1934,6 +1934,7 @@ static int mana_gd_setup(struct pci_dev *pdev)
mana_gd_remove_irqs(pdev);
free_workqueue:
destroy_workqueue(gc->service_wq);
+ gc->service_wq = NULL;
dev_err(&pdev->dev, "%s failed (error %d)\n", __func__, err);
return err;
}
--
2.50.1 (Apple Git-155)

View File

@ -0,0 +1,47 @@
From 77695a69baca9b99d95fad09fc78c2318736604f Mon Sep 17 00:00:00 2001
From: Pengpeng Hou <pengpeng@iscas.ac.cn>
Date: Wed, 25 Mar 2026 15:41:52 +0800
Subject: [PATCH] net/ipv6: ioam6: prevent schema length wraparound in trace
fill
[ Upstream commit 5e67ba9bb531e1ec6599a82a065dea9040b9ce50 ]
ioam6_fill_trace_data() stores the schema contribution to the trace
length in a u8. With bit 22 enabled and the largest schema payload,
sclen becomes 1 + 1020 / 4, wraps from 256 to 0, and bypasses the
remaining-space check. __ioam6_fill_trace_data() then positions the
write cursor without reserving the schema area but still copies the
4-byte schema header and the full schema payload, overrunning the trace
buffer.
Keep sclen in an unsigned int so the remaining-space check and the write
cursor calculation both see the full schema length.
Fixes: 8c6f6fa67726 ("ipv6: ioam: IOAM Generic Netlink API")
Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
Reviewed-by: Justin Iurman <justin.iurman@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Sasha Levin <sashal@kernel.org>
diff --git a/net/ipv6/ioam6.c b/net/ipv6/ioam6.c
index a4b7b60..bdffb79 100644
--- a/net/ipv6/ioam6.c
+++ b/net/ipv6/ioam6.c
@@ -698,7 +698,7 @@ static void __ioam6_fill_trace_data(struct sk_buff *skb,
struct ioam6_namespace *ns,
struct ioam6_trace_hdr *trace,
struct ioam6_schema *sc,
- u8 sclen, bool is_input)
+ unsigned int sclen, bool is_input)
{
struct net_device *dev = skb_dst_dev(skb);
struct timespec64 ts;
@@ -929,7 +929,7 @@ void ioam6_fill_trace_data(struct sk_buff *skb,
bool is_input)
{
struct ioam6_schema *sc;
- u8 sclen = 0;
+ unsigned int sclen = 0;
/* Skip if Overflow flag is set
*/

View File

@ -0,0 +1,63 @@
From 1d724378d2b1c0a475bc1743dbba105ded1b7a9d Mon Sep 17 00:00:00 2001
From: CKI Backport Bot <cki-ci-bot+cki-gitlab-backport-bot@redhat.com>
Date: Wed, 3 Jun 2026 19:00:19 +0000
Subject: [PATCH] procfs: fix missing RCU protection when reading real_parent
in do_task_stat()
JIRA: https://redhat.atlassian.net/browse/RHEL-181905
CVE: CVE-2026-46259
Backported from tree(s): linux
commit 76149d53502cf17ef3ae454ff384551236fba867
Author: Jinliang Zheng <alexjlzheng@tencent.com>
Date: Wed Jan 28 16:30:07 2026 +0800
procfs: fix missing RCU protection when reading real_parent in do_task_stat()
When reading /proc/[pid]/stat, do_task_stat() accesses task->real_parent
without proper RCU protection, which leads to:
cpu 0 cpu 1
----- -----
do_task_stat
var = task->real_parent
release_task
call_rcu(delayed_put_task_struct)
task_tgid_nr_ns(var)
rcu_read_lock <--- Too late to protect task->real_parent!
task_pid_ptr <--- UAF!
rcu_read_unlock
This patch uses task_ppid_nr_ns() instead of task_tgid_nr_ns() to add
proper RCU protection for accessing task->real_parent.
Link: https://lkml.kernel.org/r/20260128083007.3173016-1-alexjlzheng@tencent.com
Fixes: 06fffb1267c9 ("do_task_stat: don't take rcu_read_lock()")
Signed-off-by: Jinliang Zheng <alexjlzheng@tencent.com>
Acked-by: Oleg Nesterov <oleg@redhat.com>
Cc: David Hildenbrand <david@kernel.org>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
Cc: Mateusz Guzik <mjguzik@gmail.com>
Cc: ruippan <ruippan@tencent.com>
Cc: Usama Arif <usamaarif642@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: CKI Backport Bot <cki-ci-bot+cki-gitlab-backport-bot@redhat.com>
diff --git a/fs/proc/array.c b/fs/proc/array.c
index d6a0369caa93..11704686c097 100644
--- a/fs/proc/array.c
+++ b/fs/proc/array.c
@@ -531,7 +531,7 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns,
}
sid = task_session_nr_ns(task, ns);
- ppid = task_tgid_nr_ns(task->real_parent, ns);
+ ppid = task_ppid_nr_ns(task, ns);
pgid = task_pgrp_nr_ns(task, ns);
unlock_task_sighand(task, &flags);
--
2.50.1 (Apple Git-155)

View File

@ -0,0 +1,42 @@
From 61ea0d078a3ccf72afe38e00b891dbe48cf25c87 Mon Sep 17 00:00:00 2001
From: Thomas Walsh <thwalsh@redhat.com>
Date: Fri, 5 Jun 2026 17:21:58 -0400
Subject: [PATCH] tg3: Fix race for querying speed/duplex
JIRA: https://redhat.atlassian.net/browse/RHEL-182561
commit bb417456c7814d1493d98b7dd9c040bf3ce3b4ed
Author: Thomas Bogendoerfer <tbogendoerfer@suse.de>
Date: Wed Mar 25 12:20:53 2026 +0100
tg3: Fix race for querying speed/duplex
When driver signals carrier up via netif_carrier_on() its internal
link_up state isn't updated immediately. This leads to inconsistent
speed/duplex in /proc/net/bonding/bondX where the speed and duplex
is shown as unknown while ethtool shows correct values. Fix this by
using netif_carrier_ok() for link checking in get_ksettings function.
Fixes: 84421b99cedc ("tg3: Update link_up flag for phylib devices")
Signed-off-by: Thomas Bogendoerfer <tbogendoerfer@suse.de>
Reviewed-by: Pavan Chebbi <pavan.chebbi@broadcom.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Thomas Walsh <thwalsh@redhat.com>
diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c
index 98abd0491abe..39754ee9d393 100644
--- a/drivers/net/ethernet/broadcom/tg3.c
+++ b/drivers/net/ethernet/broadcom/tg3.c
@@ -12289,7 +12289,7 @@ static int tg3_get_link_ksettings(struct net_device *dev,
ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising,
advertising);
- if (netif_running(dev) && tp->link_up) {
+ if (netif_running(dev) && netif_carrier_ok(dev)) {
cmd->base.speed = tp->link_config.active_speed;
cmd->base.duplex = tp->link_config.active_duplex;
ethtool_convert_legacy_u32_to_link_mode(
--
2.50.1 (Apple Git-155)

View File

@ -0,0 +1,62 @@
From 14dc02b7c9eabd99199d9668ba1c0b4a29ed9a27 Mon Sep 17 00:00:00 2001
From: Paulo Alcantara <paalcant@redhat.com>
Date: Wed, 1 Jul 2026 15:54:57 -0300
Subject: [PATCH] smb: client: fix off-by-8 bounds check in check_wsl_eas()
JIRA: https://redhat.atlassian.net/browse/RHEL-180047
commit 3d8b9d06bd3ac4c6846f5498800b0f5f8062e53b
Author: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Date: Mon Apr 6 15:49:37 2026 +0200
smb: client: fix off-by-8 bounds check in check_wsl_eas()
The bounds check uses (u8 *)ea + nlen + 1 + vlen as the end of the EA
name and value, but ea_data sits at offset sizeof(struct
smb2_file_full_ea_info) = 8 from ea, not at offset 0. The strncmp()
later reads ea->ea_data[0..nlen-1] and the value bytes follow at
ea_data[nlen+1..nlen+vlen], so the actual end is ea->ea_data + nlen + 1
+ vlen. Isn't pointer math fun?
The earlier check (u8 *)ea > end - sizeof(*ea) only guarantees the
8-byte header is in bounds, but since the last EA is placed within 8
bytes of the end of the response, the name and value bytes are read past
the end of iov.
Fix this mess all up by using ea->ea_data as the base for the bounds
check.
An "untrusted" server can use this to leak up to 8 bytes of kernel heap
into the EA name comparison and influence which WSL xattr the data is
interpreted as.
Cc: Ronnie Sahlberg <ronniesahlberg@gmail.com>
Cc: Shyam Prasad N <sprasad@microsoft.com>
Cc: Tom Talpey <tom@talpey.com>
Cc: Bharath SM <bharathsm@microsoft.com>
Cc: linux-cifs@vger.kernel.org
Cc: samba-technical@lists.samba.org
Cc: stable <stable@kernel.org>
Assisted-by: gregkh_clanker_t1000
Reviewed-by: Paulo Alcantara (Red Hat) <pc@manguebit.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
Signed-off-by: Paulo Alcantara <paalcant@redhat.com>
diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c
index ea05bb4b82a1..715ff58f1742 100644
--- a/fs/smb/client/smb2inode.c
+++ b/fs/smb/client/smb2inode.c
@@ -128,7 +128,7 @@ static int check_wsl_eas(struct kvec *rsp_iov)
nlen = ea->ea_name_length;
vlen = le16_to_cpu(ea->ea_value_length);
if (nlen != SMB2_WSL_XATTR_NAME_LEN ||
- (u8 *)ea + nlen + 1 + vlen > end)
+ (u8 *)ea->ea_data + nlen + 1 + vlen > end)
return -EINVAL;
switch (vlen) {
--
2.50.1 (Apple Git-155)

View File

@ -0,0 +1,73 @@
From b4c153a8f7b3cf32752c8c0a81193535507baba7 Mon Sep 17 00:00:00 2001
From: Paulo Alcantara <paalcant@redhat.com>
Date: Wed, 1 Jul 2026 15:55:06 -0300
Subject: [PATCH] smb/client: fix out-of-bounds read in smb2_compound_op()
JIRA: https://redhat.atlassian.net/browse/RHEL-180047
CVE: CVE-2026-46155
commit 8d09328dfda089675e4c049f3f256064a1d1996b
Author: Zisen Ye <zisenye@stu.xidian.edu.cn>
Date: Wed May 6 11:49:08 2026 +0800
smb/client: fix out-of-bounds read in smb2_compound_op()
If a server sends a truncated response but a large OutputBufferLength, and
terminates the EA list early, check_wsl_eas() returns success without
validating that the entire OutputBufferLength fits within iov_len.
Then smb2_compound_op() does:
memcpy(idata->wsl.eas, data[0], size[0]);
Where size[0] is OutputBufferLength. If iov_len is smaller than size[0],
memcpy can read beyond the end of the rsp_iov allocation and leak adjacent
kernel heap memory.
Link: https://lore.kernel.org/linux-cifs/d998240c-aca9-420d-9dbd-f5ba24af19e0@chenxiaosong.com/
Fixes: ea41367b2a60 ("smb: client: introduce SMB2_OP_QUERY_WSL_EA")
Cc: stable@vger.kernel.org
Signed-off-by: Zisen Ye <zisenye@stu.xidian.edu.cn>
Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
Signed-off-by: Steve French <stfrench@microsoft.com>
Signed-off-by: Paulo Alcantara <paalcant@redhat.com>
diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c
index 715ff58f1742..2cdd46228e31 100644
--- a/fs/smb/client/smb2inode.c
+++ b/fs/smb/client/smb2inode.c
@@ -111,7 +111,7 @@ static int check_wsl_eas(struct kvec *rsp_iov)
u32 outlen, next;
u16 vlen;
u8 nlen;
- u8 *end;
+ u8 *ea_end, *iov_end;
outlen = le32_to_cpu(rsp->OutputBufferLength);
if (outlen < SMB2_WSL_MIN_QUERY_EA_RESP_SIZE ||
@@ -120,15 +120,19 @@ static int check_wsl_eas(struct kvec *rsp_iov)
ea = (void *)((u8 *)rsp_iov->iov_base +
le16_to_cpu(rsp->OutputBufferOffset));
- end = (u8 *)rsp_iov->iov_base + rsp_iov->iov_len;
+ ea_end = (u8 *)ea + outlen;
+ iov_end = (u8 *)rsp_iov->iov_base + rsp_iov->iov_len;
+ if (ea_end > iov_end)
+ return -EINVAL;
+
for (;;) {
- if ((u8 *)ea > end - sizeof(*ea))
+ if ((u8 *)ea > ea_end - sizeof(*ea))
return -EINVAL;
nlen = ea->ea_name_length;
vlen = le16_to_cpu(ea->ea_value_length);
if (nlen != SMB2_WSL_XATTR_NAME_LEN ||
- (u8 *)ea->ea_data + nlen + 1 + vlen > end)
+ (u8 *)ea->ea_data + nlen + 1 + vlen > ea_end)
return -EINVAL;
switch (vlen) {
--
2.50.1 (Apple Git-155)

View File

@ -0,0 +1,66 @@
From bde1be0c6e5eeb145576344159be1f51cb8d5d8b Mon Sep 17 00:00:00 2001
From: Ian Kent <ikent@redhat.com>
Date: Tue, 16 Jun 2026 15:53:22 +0800
Subject: [PATCH] epoll: annotate racy check
JIRA: https://redhat.atlassian.net/browse/RHEL-180777
Upstream status: Linus
commit 6474353a5e3d0b2cf610153cea0c61f576a36d0a
Author: Christian Brauner <brauner@kernel.org>
Date: Wed Sep 25 11:05:16 2024 +0200
epoll: annotate racy check
Epoll relies on a racy fastpath check during __fput() in
eventpoll_release() to avoid the hit of pointlessly acquiring a
semaphore. Annotate that race by using WRITE_ONCE() and READ_ONCE().
Link: https://lore.kernel.org/r/66edfb3c.050a0220.3195df.001a.GAE@google.com
Link: https://lore.kernel.org/r/20240925-fungieren-anbauen-79b334b00542@brauner
Reviewed-by: Jan Kara <jack@suse.cz>
Reported-by: syzbot+3b6b32dc50537a49bb4a@syzkaller.appspotmail.com
Signed-off-by: Christian Brauner <brauner@kernel.org>
Signed-off-by: Ian Kent <ikent@redhat.com>
diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index a7efeb499e4b..616f97e9b75b 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -852,7 +852,8 @@ static bool __ep_remove(struct eventpoll *ep, struct epitem *epi, bool force)
to_free = NULL;
head = file->f_ep;
if (head->first == &epi->fllink && !epi->fllink.next) {
- file->f_ep = NULL;
+ /* See eventpoll_release() for details. */
+ WRITE_ONCE(file->f_ep, NULL);
if (!is_file_epoll(file)) {
struct epitems_head *v;
v = container_of(head, struct epitems_head, epitems);
@@ -1630,7 +1631,8 @@ static int attach_epitem(struct file *file, struct epitem *epi)
spin_unlock(&file->f_lock);
goto allocate;
}
- file->f_ep = head;
+ /* See eventpoll_release() for details. */
+ WRITE_ONCE(file->f_ep, head);
to_free = NULL;
}
hlist_add_head_rcu(&epi->fllink, file->f_ep);
diff --git a/include/linux/eventpoll.h b/include/linux/eventpoll.h
index 3337745d81bd..0c0d00fcd131 100644
--- a/include/linux/eventpoll.h
+++ b/include/linux/eventpoll.h
@@ -42,7 +42,7 @@ static inline void eventpoll_release(struct file *file)
* because the file in on the way to be removed and nobody ( but
* eventpoll ) has still a reference to this file.
*/
- if (likely(!file->f_ep))
+ if (likely(!READ_ONCE(file->f_ep)))
return;
/*
--
2.50.1 (Apple Git-155)

View File

@ -0,0 +1,53 @@
From 2b62eb17ec16b6a7d341497e48e331f84280cd58 Mon Sep 17 00:00:00 2001
From: Ian Kent <ikent@redhat.com>
Date: Tue, 16 Jun 2026 15:54:24 +0800
Subject: [PATCH] eventpoll: defer struct eventpoll free to RCU grace period
JIRA: https://redhat.atlassian.net/browse/RHEL-173837
Upstream status: Linus
CVE: CVE-2026-43074
commit 07712db80857d5d09ae08f3df85a708ecfc3b61f
Author: Nicholas Carlini <nicholas@carlini.com>
Date: Tue Mar 31 15:25:32 2026 +0200
eventpoll: defer struct eventpoll free to RCU grace period
In certain situations, ep_free() in eventpoll.c will kfree the epi->ep
eventpoll struct while it still being used by another concurrent thread.
Defer the kfree() to an RCU callback to prevent UAF.
Fixes: f2e467a48287 ("eventpoll: Fix semi-unbounded recursion")
Signed-off-by: Nicholas Carlini <nicholas@carlini.com>
Signed-off-by: Christian Brauner <brauner@kernel.org>
Signed-off-by: Ian Kent <ikent@redhat.com>
diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index 616f97e9b75b..0368ab69138a 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -225,6 +225,9 @@ struct eventpoll {
*/
refcount_t refcount;
+ /* used to defer freeing past ep_get_upwards_depth_proc() RCU walk */
+ struct rcu_head rcu;
+
#ifdef CONFIG_NET_RX_BUSY_POLL
/* used to track busy poll napi_id */
unsigned int napi_id;
@@ -818,7 +821,8 @@ static void ep_free(struct eventpoll *ep)
mutex_destroy(&ep->mtx);
free_uid(ep->user);
wakeup_source_unregister(ep->ws);
- kfree(ep);
+ /* ep_get_upwards_depth_proc() may still hold epi->ep under RCU */
+ kfree_rcu(ep, rcu);
}
/*
--
2.50.1 (Apple Git-155)

View File

@ -0,0 +1,39 @@
From 1fe6204c6ea38fc1d370d9bbb9fd6a800104e5c8 Mon Sep 17 00:00:00 2001
From: Ian Kent <ikent@redhat.com>
Date: Tue, 16 Jun 2026 15:54:59 +0800
Subject: [PATCH] eventpoll: use hlist_is_singular_node() in __ep_remove()
JIRA: https://redhat.atlassian.net/browse/RHEL-180777
Upstream status: Linus
commit 3d9fd0abc94d8cd430cc7cd7d37ce5e5aae2cd2b
Author: Christian Brauner <brauner@kernel.org>
Date: Thu Apr 23 11:56:04 2026 +0200
eventpoll: use hlist_is_singular_node() in __ep_remove()
Replace the open-coded "epi is the only entry in file->f_ep" check
with hlist_is_singular_node(). Same semantics, and the helper avoids
the head-cacheline access in the common false case.
Link: https://patch.msgid.link/20260423-work-epoll-uaf-v1-1-2470f9eec0f5@kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Signed-off-by: Ian Kent <ikent@redhat.com>
diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index 0368ab69138a..2dd87800bfbf 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -855,7 +855,7 @@ static bool __ep_remove(struct eventpoll *ep, struct epitem *epi, bool force)
to_free = NULL;
head = file->f_ep;
- if (head->first == &epi->fllink && !epi->fllink.next) {
+ if (hlist_is_singular_node(&epi->fllink, head)) {
/* See eventpoll_release() for details. */
WRITE_ONCE(file->f_ep, NULL);
if (!is_file_epoll(file)) {
--
2.50.1 (Apple Git-155)

View File

@ -0,0 +1,84 @@
From 83db11e5ba3c57104945247e11c1891355a12fb6 Mon Sep 17 00:00:00 2001
From: Ian Kent <ikent@redhat.com>
Date: Tue, 16 Jun 2026 15:55:51 +0800
Subject: [PATCH] eventpoll: split __ep_remove()
JIRA: https://redhat.atlassian.net/browse/RHEL-180777
Upstream status: Linus
commit 0f7bdfd413000985de09fc39eb9efa1e091a3ce0
Author: Christian Brauner <brauner@kernel.org>
Date: Thu Apr 23 11:56:05 2026 +0200
eventpoll: split __ep_remove()
Split __ep_remove() to delineate file removal from epoll item removal.
Suggested-by: Linus Torvalds <torvalds@linux-foundation.org>
Link: https://patch.msgid.link/20260423-work-epoll-uaf-v1-2-2470f9eec0f5@kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Signed-off-by: Ian Kent <ikent@redhat.com>
diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index 2dd87800bfbf..001c87b6ddab 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -825,6 +825,9 @@ static void ep_free(struct eventpoll *ep)
kfree_rcu(ep, rcu);
}
+static void __ep_remove_file(struct eventpoll *ep, struct epitem *epi, struct file *file);
+static bool __ep_remove_epi(struct eventpoll *ep, struct epitem *epi);
+
/*
* Removes a "struct epitem" from the eventpoll RB tree and deallocates
* all the associated resources. Must be called with "mtx" held.
@@ -836,8 +839,6 @@ 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 epitems_head *to_free;
- struct hlist_head *head;
lockdep_assert_irqs_enabled();
@@ -853,8 +854,21 @@ static bool __ep_remove(struct eventpoll *ep, struct epitem *epi, bool force)
return false;
}
- to_free = NULL;
- head = file->f_ep;
+ __ep_remove_file(ep, epi, file);
+ return __ep_remove_epi(ep, epi);
+}
+
+/*
+ * Called with &file->f_lock held,
+ * returns with it released
+ */
+static void __ep_remove_file(struct eventpoll *ep, struct epitem *epi, struct file *file)
+{
+ struct epitems_head *to_free = NULL;
+ struct hlist_head *head = file->f_ep;
+
+ lockdep_assert_held(&ep->mtx);
+
if (hlist_is_singular_node(&epi->fllink, head)) {
/* See eventpoll_release() for details. */
WRITE_ONCE(file->f_ep, NULL);
@@ -868,6 +882,11 @@ static bool __ep_remove(struct eventpoll *ep, struct epitem *epi, bool force)
hlist_del_rcu(&epi->fllink);
spin_unlock(&file->f_lock);
free_ephead(to_free);
+}
+
+static bool __ep_remove_epi(struct eventpoll *ep, struct epitem *epi)
+{
+ lockdep_assert_held(&ep->mtx);
rb_erase_cached(&epi->rbn, &ep->rbr);
--
2.50.1 (Apple Git-155)

View File

@ -0,0 +1,134 @@
From 9a9a41f8aa46741f965703f520542e9e8fdbe59f Mon Sep 17 00:00:00 2001
From: Ian Kent <ikent@redhat.com>
Date: Tue, 16 Jun 2026 15:56:21 +0800
Subject: [PATCH] eventpoll: kill __ep_remove()
JIRA: https://redhat.atlassian.net/browse/RHEL-180777
Upstream status: Linus
commit e9e5cd40d7c403e19f21d0f7b8b8ba3a76b58330
Author: Christian Brauner <brauner@kernel.org>
Date: Thu Apr 23 11:56:06 2026 +0200
eventpoll: kill __ep_remove()
Remove the boolean conditional in __ep_remove() and restructure the code
so the check for racing with eventpoll_release_file() are only done in
the ep_remove_safe() path where they belong.
Link: https://patch.msgid.link/20260423-work-epoll-uaf-v1-3-2470f9eec0f5@kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Signed-off-by: Ian Kent <ikent@redhat.com>
diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index 001c87b6ddab..cb75868ba1de 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -825,49 +825,18 @@ static void ep_free(struct eventpoll *ep)
kfree_rcu(ep, rcu);
}
-static void __ep_remove_file(struct eventpoll *ep, struct epitem *epi, struct file *file);
-static bool __ep_remove_epi(struct eventpoll *ep, struct epitem *epi);
-
-/*
- * Removes a "struct epitem" from the eventpoll RB tree and deallocates
- * all the associated resources. Must be called with "mtx" held.
- * If the dying flag is set, do the removal only if force is true.
- * This prevents ep_clear_and_put() from dropping all the ep references
- * while running concurrently with eventpoll_release_file().
- * Returns true if the eventpoll can be disposed.
- */
-static bool __ep_remove(struct eventpoll *ep, struct epitem *epi, bool force)
-{
- struct file *file = epi->ffd.file;
-
- lockdep_assert_irqs_enabled();
-
- /*
- * Removes poll wait queue hooks.
- */
- 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;
- }
-
- __ep_remove_file(ep, epi, file);
- return __ep_remove_epi(ep, epi);
-}
-
/*
* Called with &file->f_lock held,
* returns with it released
*/
-static void __ep_remove_file(struct eventpoll *ep, struct epitem *epi, struct file *file)
+static void __ep_remove_file(struct eventpoll *ep, struct epitem *epi,
+ struct file *file)
{
struct epitems_head *to_free = NULL;
struct hlist_head *head = file->f_ep;
lockdep_assert_held(&ep->mtx);
+ lockdep_assert_held(&file->f_lock);
if (hlist_is_singular_node(&epi->fllink, head)) {
/* See eventpoll_release() for details. */
@@ -914,7 +883,25 @@ static bool __ep_remove_epi(struct eventpoll *ep, struct epitem *epi)
*/
static void ep_remove_safe(struct eventpoll *ep, struct epitem *epi)
{
- if (__ep_remove(ep, epi, false))
+ struct file *file = epi->ffd.file;
+
+ lockdep_assert_irqs_enabled();
+ lockdep_assert_held(&ep->mtx);
+
+ ep_unregister_pollwait(ep, epi);
+
+ /* sync with eventpoll_release_file() */
+ if (unlikely(READ_ONCE(epi->dying)))
+ return;
+
+ spin_lock(&file->f_lock);
+ if (epi->dying) {
+ spin_unlock(&file->f_lock);
+ return;
+ }
+ __ep_remove_file(ep, epi, file);
+
+ if (__ep_remove_epi(ep, epi))
WARN_ON_ONCE(ep_refcount_dec_and_test(ep));
}
@@ -1146,7 +1133,7 @@ void eventpoll_release_file(struct file *file)
spin_lock(&file->f_lock);
if (file->f_ep && file->f_ep->first) {
epi = hlist_entry(file->f_ep->first, struct epitem, fllink);
- epi->dying = true;
+ WRITE_ONCE(epi->dying, true);
spin_unlock(&file->f_lock);
/*
@@ -1155,7 +1142,13 @@ void eventpoll_release_file(struct file *file)
*/
ep = epi->ep;
mutex_lock(&ep->mtx);
- dispose = __ep_remove(ep, epi, true);
+
+ ep_unregister_pollwait(ep, epi);
+
+ spin_lock(&file->f_lock);
+ __ep_remove_file(ep, epi, file);
+ dispose = __ep_remove_epi(ep, epi);
+
mutex_unlock(&ep->mtx);
if (dispose && ep_refcount_dec_and_test(ep))
--
2.50.1 (Apple Git-155)

View File

@ -0,0 +1,98 @@
From 4e16f36e52dfe03056ec8c4daaef79b1715bb341 Mon Sep 17 00:00:00 2001
From: Ian Kent <ikent@redhat.com>
Date: Tue, 16 Jun 2026 15:58:37 +0800
Subject: [PATCH] eventpoll: rename ep_remove_safe() back to ep_remove()
JIRA: https://redhat.atlassian.net/browse/RHEL-180777
Upstream status: Linus
commit 0bade234723e40e4937be912e105785d6a51464e
Author: Christian Brauner <brauner@kernel.org>
Date: Thu Apr 23 11:56:07 2026 +0200
eventpoll: rename ep_remove_safe() back to ep_remove()
The current name is just confusing and doesn't clarify anything.
Link: https://patch.msgid.link/20260423-work-epoll-uaf-v1-4-2470f9eec0f5@kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Signed-off-by: Ian Kent <ikent@redhat.com>
diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index cb75868ba1de..efc9e3e1e3f5 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -881,7 +881,7 @@ static bool __ep_remove_epi(struct eventpoll *ep, struct epitem *epi)
/*
* ep_remove variant for callers owing an additional reference to the ep
*/
-static void ep_remove_safe(struct eventpoll *ep, struct epitem *epi)
+static void ep_remove(struct eventpoll *ep, struct epitem *epi)
{
struct file *file = epi->ffd.file;
@@ -928,7 +928,7 @@ static void ep_clear_and_put(struct eventpoll *ep)
/*
* Walks through the whole tree and try to free each "struct epitem".
- * Note that ep_remove_safe() will not remove the epitem in case of a
+ * Note that ep_remove() will not remove the epitem in case of a
* racing eventpoll_release_file(); the latter will do the removal.
* At this point we are sure no poll callbacks will be lingering around.
* Since we still own a reference to the eventpoll struct, the loop can't
@@ -937,7 +937,7 @@ static void ep_clear_and_put(struct eventpoll *ep)
for (rbp = rb_first_cached(&ep->rbr); rbp; rbp = next) {
next = rb_next(rbp);
epi = rb_entry(rbp, struct epitem, rbn);
- ep_remove_safe(ep, epi);
+ ep_remove(ep, epi);
cond_resched();
}
@@ -1714,21 +1714,21 @@ static int ep_insert(struct eventpoll *ep, const struct epoll_event *event,
mutex_unlock(&tep->mtx);
/*
- * ep_remove_safe() calls in the later error paths can't lead to
+ * ep_remove() calls in the later error paths can't lead to
* ep_free() as the ep file itself still holds an ep reference.
*/
ep_get(ep);
/* now check if we've created too many backpaths */
if (unlikely(full_check && reverse_path_check())) {
- ep_remove_safe(ep, epi);
+ ep_remove(ep, epi);
return -EINVAL;
}
if (epi->event.events & EPOLLWAKEUP) {
error = ep_create_wakeup_source(epi);
if (error) {
- ep_remove_safe(ep, epi);
+ ep_remove(ep, epi);
return error;
}
}
@@ -1752,7 +1752,7 @@ static int ep_insert(struct eventpoll *ep, const struct epoll_event *event,
* high memory pressure.
*/
if (unlikely(!epq.epi)) {
- ep_remove_safe(ep, epi);
+ ep_remove(ep, epi);
return -ENOMEM;
}
@@ -2415,7 +2415,7 @@ int do_epoll_ctl(int epfd, int op, int fd, struct epoll_event *epds,
* The eventpoll itself is still alive: the refcount
* can't go to zero here.
*/
- ep_remove_safe(ep, epi);
+ ep_remove(ep, epi);
error = 0;
} else {
error = -ENOENT;
--
2.50.1 (Apple Git-155)

View File

@ -0,0 +1,98 @@
From 8c1707336e5674c16c2a24c9cdf2b5018b166a09 Mon Sep 17 00:00:00 2001
From: Ian Kent <ikent@redhat.com>
Date: Tue, 16 Jun 2026 16:00:26 +0800
Subject: [PATCH] eventpoll: move epi_fget() up
JIRA: https://redhat.atlassian.net/browse/RHEL-180777
Upstream status: Linus
commit 86e87059e6d1fd5115a31949726450ed03c1073b
Author: Christian Brauner <brauner@kernel.org>
Date: Thu Apr 23 11:56:08 2026 +0200
eventpoll: move epi_fget() up
We'll need it when removing files so move it up. No functional change.
Link: https://patch.msgid.link/20260423-work-epoll-uaf-v1-5-2470f9eec0f5@kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Signed-off-by: Ian Kent <ikent@redhat.com>
diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index efc9e3e1e3f5..ded53c6fbd6f 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -825,6 +825,34 @@ static void ep_free(struct eventpoll *ep)
kfree_rcu(ep, rcu);
}
+/*
+ * The ffd.file pointer may be in the process of being torn down due to
+ * being closed, but we may not have finished eventpoll_release() yet.
+ *
+ * Normally, even with the atomic_long_inc_not_zero, the file may have
+ * been free'd and then gotten re-allocated to something else (since
+ * files are not RCU-delayed, they are SLAB_TYPESAFE_BY_RCU).
+ *
+ * But for epoll, users hold the ep->mtx mutex, and as such any file in
+ * the process of being free'd will block in eventpoll_release_file()
+ * and thus the underlying file allocation will not be free'd, and the
+ * file re-use cannot happen.
+ *
+ * For the same reason we can avoid a rcu_read_lock() around the
+ * operation - 'ffd.file' cannot go away even if the refcount has
+ * reached zero (but we must still not call out to ->poll() functions
+ * etc).
+ */
+static struct file *epi_fget(const struct epitem *epi)
+{
+ struct file *file;
+
+ file = epi->ffd.file;
+ if (!file_ref_get(&file->f_ref))
+ file = NULL;
+ return file;
+}
+
/*
* Called with &file->f_lock held,
* returns with it released
@@ -1017,34 +1045,6 @@ static __poll_t __ep_eventpoll_poll(struct file *file, poll_table *wait, int dep
return res;
}
-/*
- * The ffd.file pointer may be in the process of being torn down due to
- * being closed, but we may not have finished eventpoll_release() yet.
- *
- * Normally, even with the atomic_long_inc_not_zero, the file may have
- * been free'd and then gotten re-allocated to something else (since
- * files are not RCU-delayed, they are SLAB_TYPESAFE_BY_RCU).
- *
- * But for epoll, users hold the ep->mtx mutex, and as such any file in
- * the process of being free'd will block in eventpoll_release_file()
- * and thus the underlying file allocation will not be free'd, and the
- * file re-use cannot happen.
- *
- * For the same reason we can avoid a rcu_read_lock() around the
- * operation - 'ffd.file' cannot go away even if the refcount has
- * reached zero (but we must still not call out to ->poll() functions
- * etc).
- */
-static struct file *epi_fget(const struct epitem *epi)
-{
- struct file *file;
-
- file = epi->ffd.file;
- if (!file_ref_get(&file->f_ref))
- file = NULL;
- return file;
-}
-
/*
* Differs from ep_eventpoll_poll() in that internal callers already have
* the ep->mtx so we need to start from depth=1, such that mutex_lock_nested()
--
2.50.1 (Apple Git-155)

View File

@ -0,0 +1,78 @@
From 6175a9a692e559e7f0635d3a32d20fe94bae8280 Mon Sep 17 00:00:00 2001
From: Ian Kent <ikent@redhat.com>
Date: Tue, 16 Jun 2026 15:57:24 +0800
Subject: [PATCH] eventpoll: drop vestigial __ prefix from
ep_remove_{file,epi}()
JIRA: https://redhat.atlassian.net/browse/RHEL-180777
Upstream status: Linus
Conflict: I don't know how this commit is present when commit
d3608d9e2f5a ("eventpoll: fix ep_remove struct eventpoll /
struct file UAF") is applied since its time stamp is later than
the afore mentioned commit. I've applied it here purely to avoid
the subsequent conflict.
commit 0feaf644f7180c4a91b6b405a881afbfd958f1cf
Author: Christian Brauner <brauner@kernel.org>
Date: Fri Apr 24 00:23:18 2026 +0200
eventpoll: drop vestigial __ prefix from ep_remove_{file,epi}()
With __ep_remove() gone, the double-underscore on __ep_remove_file()
and __ep_remove_epi() no longer contrasts with a __-less parent and
just reads as noise. Rename both to ep_remove_file() and
ep_remove_epi(). No functional change.
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Signed-off-by: Ian Kent <ikent@redhat.com>
diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index ded53c6fbd6f..1246c0517aa6 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -857,7 +857,7 @@ static struct file *epi_fget(const struct epitem *epi)
* Called with &file->f_lock held,
* returns with it released
*/
-static void __ep_remove_file(struct eventpoll *ep, struct epitem *epi,
+static void ep_remove_file(struct eventpoll *ep, struct epitem *epi,
struct file *file)
{
struct epitems_head *to_free = NULL;
@@ -881,7 +881,7 @@ static void __ep_remove_file(struct eventpoll *ep, struct epitem *epi,
free_ephead(to_free);
}
-static bool __ep_remove_epi(struct eventpoll *ep, struct epitem *epi)
+static bool ep_remove_epi(struct eventpoll *ep, struct epitem *epi)
{
lockdep_assert_held(&ep->mtx);
@@ -927,9 +927,9 @@ static void ep_remove(struct eventpoll *ep, struct epitem *epi)
spin_unlock(&file->f_lock);
return;
}
- __ep_remove_file(ep, epi, file);
+ ep_remove_file(ep, epi, file);
- if (__ep_remove_epi(ep, epi))
+ if (ep_remove_epi(ep, epi))
WARN_ON_ONCE(ep_refcount_dec_and_test(ep));
}
@@ -1146,8 +1146,8 @@ void eventpoll_release_file(struct file *file)
ep_unregister_pollwait(ep, epi);
spin_lock(&file->f_lock);
- __ep_remove_file(ep, epi, file);
- dispose = __ep_remove_epi(ep, epi);
+ ep_remove_file(ep, epi, file);
+ dispose = ep_remove_epi(ep, epi);
mutex_unlock(&ep->mtx);
--
2.50.1 (Apple Git-155)

View File

@ -0,0 +1,104 @@
From 5c53e2d149e55d3ee64fb03387d2f81d59477774 Mon Sep 17 00:00:00 2001
From: Ian Kent <ikent@redhat.com>
Date: Tue, 16 Jun 2026 16:00:59 +0800
Subject: [PATCH] eventpoll: fix ep_remove struct eventpoll / struct file UAF
JIRA: https://redhat.atlassian.net/browse/RHEL-180777
Upstream status: Linus
CVE: CVE-2026-46242
commit a6dc643c69311677c574a0f17a3f4d66a5f3744b
Author: Christian Brauner <brauner@kernel.org>
Date: Thu Apr 23 11:56:09 2026 +0200
eventpoll: fix ep_remove struct eventpoll / struct file UAF
ep_remove() (via ep_remove_file()) cleared file->f_ep under
file->f_lock but then kept using @file inside the critical section
(is_file_epoll(), hlist_del_rcu() through the head, spin_unlock).
A concurrent __fput() taking the eventpoll_release() fastpath in
that window observed the transient NULL, skipped
eventpoll_release_file() and ran to f_op->release / file_free().
For the epoll-watches-epoll case, f_op->release is
ep_eventpoll_release() -> ep_clear_and_put() -> ep_free(), which
kfree()s the watched struct eventpoll. Its embedded ->refs
hlist_head is exactly where epi->fllink.pprev points, so the
subsequent hlist_del_rcu()'s "*pprev = next" scribbles into freed
kmalloc-192 memory.
In addition, struct file is SLAB_TYPESAFE_BY_RCU, so the slot
backing @file could be recycled by alloc_empty_file() --
reinitializing f_lock and f_ep -- while ep_remove() is still
nominally inside that lock. The upshot is an attacker-controllable
kmem_cache_free() against the wrong slab cache.
Pin @file via epi_fget() at the top of ep_remove() and gate the
critical section on the pin succeeding. With the pin held @file
cannot reach refcount zero, which holds __fput() off and
transitively keeps the watched struct eventpoll alive across the
hlist_del_rcu() and the f_lock use, closing both UAFs.
If the pin fails @file has already reached refcount zero and its
__fput() is in flight. Because we bailed before clearing f_ep,
that path takes the eventpoll_release() slow path into
eventpoll_release_file() and blocks on ep->mtx until the waiter
side's ep_clear_and_put() drops it. The bailed epi's share of
ep->refcount stays intact, so the trailing ep_refcount_dec_and_test()
in ep_clear_and_put() cannot free the eventpoll out from under
eventpoll_release_file(); the orphaned epi is then cleaned up
there.
A successful pin also proves we are not racing
eventpoll_release_file() on this epi, so drop the now-redundant
re-check of epi->dying under f_lock. The cheap lockless
READ_ONCE(epi->dying) fast-path bailout stays.
Fixes: 58c9b016e128 ("epoll: use refcount to reduce ep_mutex contention")
Reported-by: Jaeyoung Chung <jjy600901@snu.ac.kr>
Link: https://patch.msgid.link/20260423-work-epoll-uaf-v1-6-2470f9eec0f5@kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Signed-off-by: Ian Kent <ikent@redhat.com>
diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index 1246c0517aa6..f46906640a9b 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -911,22 +911,26 @@ static bool ep_remove_epi(struct eventpoll *ep, struct epitem *epi)
*/
static void ep_remove(struct eventpoll *ep, struct epitem *epi)
{
- struct file *file = epi->ffd.file;
+ struct file *file __free(fput) = NULL;
lockdep_assert_irqs_enabled();
lockdep_assert_held(&ep->mtx);
ep_unregister_pollwait(ep, epi);
- /* sync with eventpoll_release_file() */
+ /* cheap sync with eventpoll_release_file() */
if (unlikely(READ_ONCE(epi->dying)))
return;
- spin_lock(&file->f_lock);
- if (epi->dying) {
- spin_unlock(&file->f_lock);
+ /*
+ * If we manage to grab a reference it means we're not in
+ * eventpoll_release_file() and aren't going to be.
+ */
+ file = epi_fget(epi);
+ if (!file)
return;
- }
+
+ spin_lock(&file->f_lock);
ep_remove_file(ep, epi, file);
if (ep_remove_epi(ep, epi))
--
2.50.1 (Apple Git-155)

View File

@ -0,0 +1,70 @@
From e0bf480728b83ec6ceb66975bfbac514195873c8 Mon Sep 17 00:00:00 2001
From: Ian Kent <ikent@redhat.com>
Date: Tue, 16 Jun 2026 16:01:51 +0800
Subject: [PATCH] eventpoll: move f_lock acquisition into ep_remove_file()
JIRA: https://redhat.atlassian.net/browse/RHEL-180777
Upstream status: Linus
commit d30deeb8b0cf6259785c1fb79b87905d281b0a5a
Author: Christian Brauner <brauner@kernel.org>
Date: Thu Apr 23 11:56:10 2026 +0200
eventpoll: move f_lock acquisition into ep_remove_file()
Let the helper own its critical section end-to-end: take &file->f_lock
at the top, read file->f_ep inside the lock, release on exit. Callers
(ep_remove() and eventpoll_release_file()) no longer need to wrap the
call, and the function-comment lock-handoff contract is gone.
Link: https://patch.msgid.link/20260423-work-epoll-uaf-v1-7-2470f9eec0f5@kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Signed-off-by: Ian Kent <ikent@redhat.com>
diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index f46906640a9b..9798ab5f7663 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -854,18 +854,18 @@ static struct file *epi_fget(const struct epitem *epi)
}
/*
- * Called with &file->f_lock held,
- * returns with it released
+ * Takes &file->f_lock; returns with it released.
*/
static void ep_remove_file(struct eventpoll *ep, struct epitem *epi,
struct file *file)
{
struct epitems_head *to_free = NULL;
- struct hlist_head *head = file->f_ep;
+ struct hlist_head *head;
lockdep_assert_held(&ep->mtx);
- lockdep_assert_held(&file->f_lock);
+ spin_lock(&file->f_lock);
+ head = file->f_ep;
if (hlist_is_singular_node(&epi->fllink, head)) {
/* See eventpoll_release() for details. */
WRITE_ONCE(file->f_ep, NULL);
@@ -930,7 +930,6 @@ static void ep_remove(struct eventpoll *ep, struct epitem *epi)
if (!file)
return;
- spin_lock(&file->f_lock);
ep_remove_file(ep, epi, file);
if (ep_remove_epi(ep, epi))
@@ -1149,7 +1148,6 @@ void eventpoll_release_file(struct file *file)
ep_unregister_pollwait(ep, epi);
- spin_lock(&file->f_lock);
ep_remove_file(ep, epi, file);
dispose = ep_remove_epi(ep, epi);
--
2.50.1 (Apple Git-155)

View File

@ -0,0 +1,66 @@
From eab8e8be1be2430b6712fc1a6c8925d240a40031 Mon Sep 17 00:00:00 2001
From: Ian Kent <ikent@redhat.com>
Date: Tue, 16 Jun 2026 16:02:27 +0800
Subject: [PATCH] eventpoll: refresh eventpoll_release() fast-path comment
JIRA: https://redhat.atlassian.net/browse/RHEL-180777
Upstream status: Linus
commit 33e92e9ecf48c08cb4807e9a36f9eb01619c1a1e
Author: Christian Brauner <brauner@kernel.org>
Date: Thu Apr 23 11:56:11 2026 +0200
eventpoll: refresh eventpoll_release() fast-path comment
The old comment justified the lockless READ_ONCE(file->f_ep) check
with "False positives simply cannot happen because the file is on
the way to be removed and nobody ( but eventpoll ) has still a
reference to this file." That reasoning was the root of the UAF
fixed in "eventpoll: fix ep_remove struct eventpoll / struct file
UAF": __ep_remove() could clear f_ep while another close raced
past the fast path and freed the watched eventpoll / recycled the
struct file slot.
With ep_remove() now pinning @file via epi_fget() across the f_ep
clear and hlist_del_rcu(), the invariant is re-established for the
right reason: anyone who might clear f_ep holds @file alive for
the duration, so a NULL observation really does mean no
concurrent eventpoll path has work left on this file. Refresh the
comment accordingly so the next reader doesn't inherit the broken
model.
Link: https://patch.msgid.link/20260423-work-epoll-uaf-v1-8-2470f9eec0f5@kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Signed-off-by: Ian Kent <ikent@redhat.com>
diff --git a/include/linux/eventpoll.h b/include/linux/eventpoll.h
index 0c0d00fcd131..be762281355a 100644
--- a/include/linux/eventpoll.h
+++ b/include/linux/eventpoll.h
@@ -35,12 +35,16 @@ static inline void eventpoll_release(struct file *file)
{
/*
- * Fast check to avoid the get/release of the semaphore. Since
- * we're doing this outside the semaphore lock, it might return
- * false negatives, but we don't care. It'll help in 99.99% of cases
- * to avoid the semaphore lock. False positives simply cannot happen
- * because the file in on the way to be removed and nobody ( but
- * eventpoll ) has still a reference to this file.
+ * Fast check to skip the slow path in the common case where the
+ * file was never attached to an epoll. Safe without file->f_lock
+ * because every f_ep writer excludes a concurrent __fput() on
+ * @file:
+ * - ep_insert() requires the file alive (refcount > 0);
+ * - ep_remove() holds @file pinned via epi_fget() across the
+ * write;
+ * - eventpoll_release_file() runs from __fput() itself.
+ * We are in __fput() here, so none of those can race us: a NULL
+ * observation truly means no epoll path has work left on @file.
*/
if (likely(!READ_ONCE(file->f_ep)))
return;
--
2.50.1 (Apple Git-155)

View File

@ -0,0 +1,95 @@
From 38a78d49b434ce9f29afdfaa870142ecb121800f Mon Sep 17 00:00:00 2001
From: Ian Kent <ikent@redhat.com>
Date: Tue, 16 Jun 2026 16:03:04 +0800
Subject: [PATCH] eventpoll: drop dead bool return from ep_remove_epi()
JIRA: https://redhat.atlassian.net/browse/RHEL-180777
Upstream status: Linus
commit 3a4551ea9c042502019b1d8a986e962cb9015366
Author: Christian Brauner <brauner@kernel.org>
Date: Thu Apr 23 11:56:12 2026 +0200
eventpoll: drop dead bool return from ep_remove_epi()
ep_remove_epi() always returns true -- the "can be disposed"
answer was meaningful back when the dying-check lived inside the
pre-split __ep_remove(), but after that check moved to ep_remove()
the return value is just noise. Both callers gate on it
unconditionally:
if (ep_remove_epi(ep, epi))
WARN_ON_ONCE(ep_refcount_dec_and_test(ep));
dispose = ep_remove_epi(ep, epi);
...
if (dispose && ep_refcount_dec_and_test(ep))
ep_free(ep);
Make ep_remove_epi() return void, drop the dispose local in
eventpoll_release_file(), and the useless conditionals at both
callers. No functional change.
Link: https://patch.msgid.link/20260423-work-epoll-uaf-v1-9-2470f9eec0f5@kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Signed-off-by: Ian Kent <ikent@redhat.com>
diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index 9798ab5f7663..761bd85f213e 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -881,7 +881,7 @@ static void ep_remove_file(struct eventpoll *ep, struct epitem *epi,
free_ephead(to_free);
}
-static bool ep_remove_epi(struct eventpoll *ep, struct epitem *epi)
+static void ep_remove_epi(struct eventpoll *ep, struct epitem *epi)
{
lockdep_assert_held(&ep->mtx);
@@ -903,7 +903,6 @@ static bool ep_remove_epi(struct eventpoll *ep, struct epitem *epi)
kfree_rcu(epi, rcu);
percpu_counter_dec(&ep->user->epoll_watches);
- return true;
}
/*
@@ -931,9 +930,8 @@ static void ep_remove(struct eventpoll *ep, struct epitem *epi)
return;
ep_remove_file(ep, epi, file);
-
- if (ep_remove_epi(ep, epi))
- WARN_ON_ONCE(ep_refcount_dec_and_test(ep));
+ ep_remove_epi(ep, epi);
+ WARN_ON_ONCE(ep_refcount_dec_and_test(ep));
}
static void ep_clear_and_put(struct eventpoll *ep)
@@ -1125,7 +1123,6 @@ void eventpoll_release_file(struct file *file)
{
struct eventpoll *ep;
struct epitem *epi;
- bool dispose;
/*
* Use the 'dying' flag to prevent a concurrent ep_clear_and_put() from
@@ -1149,11 +1146,11 @@ void eventpoll_release_file(struct file *file)
ep_unregister_pollwait(ep, epi);
ep_remove_file(ep, epi, file);
- dispose = ep_remove_epi(ep, epi);
+ ep_remove_epi(ep, epi);
mutex_unlock(&ep->mtx);
- if (dispose && ep_refcount_dec_and_test(ep))
+ if (ep_refcount_dec_and_test(ep))
ep_free(ep);
goto again;
}
--
2.50.1 (Apple Git-155)

View File

@ -0,0 +1,176 @@
From 6ee3e28a7271944ba136a37d07ca35f0f7e3903b Mon Sep 17 00:00:00 2001
From: Ian Kent <ikent@redhat.com>
Date: Tue, 16 Jun 2026 16:03:26 +0800
Subject: [PATCH] eventpoll: Fix semi-unbounded recursion
JIRA: https://redhat.atlassian.net/browse/RHEL-180777
Upstream status: Linus
CVE: CVE-2025-38614
commit f2e467a48287c868818085aa35389a224d226732
Author: Jann Horn <jannh@google.com>
Date: Fri Jul 11 18:33:36 2025 +0200
eventpoll: Fix semi-unbounded recursion
Ensure that epoll instances can never form a graph deeper than
EP_MAX_NESTS+1 links.
Currently, ep_loop_check_proc() ensures that the graph is loop-free and
does some recursion depth checks, but those recursion depth checks don't
limit the depth of the resulting tree for two reasons:
- They don't look upwards in the tree.
- If there are multiple downwards paths of different lengths, only one of
the paths is actually considered for the depth check since commit
28d82dc1c4ed ("epoll: limit paths").
Essentially, the current recursion depth check in ep_loop_check_proc() just
serves to prevent it from recursing too deeply while checking for loops.
A more thorough check is done in reverse_path_check() after the new graph
edge has already been created; this checks, among other things, that no
paths going upwards from any non-epoll file with a length of more than 5
edges exist. However, this check does not apply to non-epoll files.
As a result, it is possible to recurse to a depth of at least roughly 500,
tested on v6.15. (I am unsure if deeper recursion is possible; and this may
have changed with commit 8c44dac8add7 ("eventpoll: Fix priority inversion
problem").)
To fix it:
1. In ep_loop_check_proc(), note the subtree depth of each visited node,
and use subtree depths for the total depth calculation even when a subtree
has already been visited.
2. Add ep_get_upwards_depth_proc() for similarly determining the maximum
depth of an upwards walk.
3. In ep_loop_check(), use these values to limit the total path length
between epoll nodes to EP_MAX_NESTS edges.
Fixes: 22bacca48a17 ("epoll: prevent creating circular epoll structures")
Cc: stable@vger.kernel.org
Signed-off-by: Jann Horn <jannh@google.com>
Link: https://lore.kernel.org/20250711-epoll-recursion-fix-v1-1-fb2457c33292@google.com
Signed-off-by: Christian Brauner <brauner@kernel.org>
Signed-off-by: Ian Kent <ikent@redhat.com>
diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index 761bd85f213e..88b501a5e709 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -218,6 +218,7 @@ struct eventpoll {
/* used to optimize loop detection check */
u64 gen;
struct hlist_head refs;
+ u8 loop_check_depth;
/*
* usage count, used together with epitem->dying to
@@ -2135,23 +2136,24 @@ static int ep_poll(struct eventpoll *ep, struct epoll_event __user *events,
}
/**
- * ep_loop_check_proc - verify that adding an epoll file inside another
- * epoll structure does not violate the constraints, in
- * terms of closed loops, or too deep chains (which can
- * result in excessive stack usage).
+ * ep_loop_check_proc - verify that adding an epoll file @ep inside another
+ * epoll file does not create closed loops, and
+ * determine the depth of the subtree starting at @ep
*
* @ep: the &struct eventpoll to be currently checked.
* @depth: Current depth of the path being checked.
*
- * Return: %zero if adding the epoll @file inside current epoll
- * structure @ep does not violate the constraints, or %-1 otherwise.
+ * Return: depth of the subtree, or INT_MAX if we found a loop or went too deep.
*/
static int ep_loop_check_proc(struct eventpoll *ep, int depth)
{
- int error = 0;
+ int result = 0;
struct rb_node *rbp;
struct epitem *epi;
+ if (ep->gen == loop_check_gen)
+ return ep->loop_check_depth;
+
mutex_lock_nested(&ep->mtx, depth + 1);
ep->gen = loop_check_gen;
for (rbp = rb_first_cached(&ep->rbr); rbp; rbp = rb_next(rbp)) {
@@ -2159,13 +2161,11 @@ static int ep_loop_check_proc(struct eventpoll *ep, int depth)
if (unlikely(is_file_epoll(epi->ffd.file))) {
struct eventpoll *ep_tovisit;
ep_tovisit = epi->ffd.file->private_data;
- if (ep_tovisit->gen == loop_check_gen)
- continue;
if (ep_tovisit == inserting_into || depth > EP_MAX_NESTS)
- error = -1;
+ result = INT_MAX;
else
- error = ep_loop_check_proc(ep_tovisit, depth + 1);
- if (error != 0)
+ result = max(result, ep_loop_check_proc(ep_tovisit, depth + 1) + 1);
+ if (result > EP_MAX_NESTS)
break;
} else {
/*
@@ -2179,9 +2179,27 @@ static int ep_loop_check_proc(struct eventpoll *ep, int depth)
list_file(epi->ffd.file);
}
}
+ ep->loop_check_depth = result;
mutex_unlock(&ep->mtx);
- return error;
+ return result;
+}
+
+/**
+ * ep_get_upwards_depth_proc - determine depth of @ep when traversed upwards
+ */
+static int ep_get_upwards_depth_proc(struct eventpoll *ep, int depth)
+{
+ int result = 0;
+ struct epitem *epi;
+
+ if (ep->gen == loop_check_gen)
+ return ep->loop_check_depth;
+ hlist_for_each_entry_rcu(epi, &ep->refs, fllink)
+ result = max(result, ep_get_upwards_depth_proc(epi->ep, depth + 1) + 1);
+ ep->gen = loop_check_gen;
+ ep->loop_check_depth = result;
+ return result;
}
/**
@@ -2197,8 +2215,22 @@ static int ep_loop_check_proc(struct eventpoll *ep, int depth)
*/
static int ep_loop_check(struct eventpoll *ep, struct eventpoll *to)
{
+ int depth, upwards_depth;
+
inserting_into = ep;
- return ep_loop_check_proc(to, 0);
+ /*
+ * Check how deep down we can get from @to, and whether it is possible
+ * to loop up to @ep.
+ */
+ depth = ep_loop_check_proc(to, 0);
+ if (depth > EP_MAX_NESTS)
+ return -1;
+ /* Check how far up we can go from @ep. */
+ rcu_read_lock();
+ upwards_depth = ep_get_upwards_depth_proc(ep, 0);
+ rcu_read_unlock();
+
+ return (depth+1+upwards_depth > EP_MAX_NESTS) ? -1 : 0;
}
static void clear_tfile_check_list(void)
--
2.50.1 (Apple Git-155)

View File

@ -0,0 +1,110 @@
From 3724dc6414c5da5c393e58e0e5863fdb95166922 Mon Sep 17 00:00:00 2001
From: Ian Kent <ikent@redhat.com>
Date: Tue, 16 Jun 2026 16:03:26 +0800
Subject: [PATCH] eventpoll: drop vestigial epi->dying flag
JIRA: https://redhat.atlassian.net/browse/RHEL-180777
Upstream status: Linus
commit 07422c948f4bdf15567a129a0983f7c12e57ba8e
Author: Christian Brauner <brauner@kernel.org>
Date: Thu Apr 23 11:56:13 2026 +0200
eventpoll: drop vestigial epi->dying flag
With ep_remove() now pinning @file via epi_fget() across the
f_ep clear and hlist_del_rcu(), the dying flag no longer
orchestrates anything: it was set in eventpoll_release_file()
(which only runs from __fput(), i.e. after @file's refcount has
reached zero) and read in __ep_remove() / ep_remove() as a cheap
bail before attempting the same synchronization epi_fget() now
provides unconditionally.
The implication is simple: epi->dying == true always coincides
with file_ref_get(&file->f_ref) == false, because __fput() is
reachable only once the refcount hits zero and the refcount is
monotone in that state. The READ_ONCE(epi->dying) in ep_remove()
therefore selects exactly the same callers that epi_fget() would
reject, just one atomic cheaper. That's not worth a struct
field, a second coordination mechanism, and the comments on
both.
Refresh the eventpoll_release_file() comment to describe what
actually makes the path race-free now (the pin in ep_remove()).
No functional change: the correctness argument is unchanged,
only the mechanism is now a single one instead of two.
Link: https://patch.msgid.link/20260423-work-epoll-uaf-v1-10-2470f9eec0f5@kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Signed-off-by: Ian Kent <ikent@redhat.com>
diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index 88b501a5e709..5228ff9c8742 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -148,13 +148,6 @@ struct epitem {
/* The file descriptor information this item refers to */
struct epoll_filefd ffd;
- /*
- * Protected by file->f_lock, true for to-be-released epitem already
- * removed from the "struct file" items list; together with
- * eventpoll->refcount orchestrates "struct eventpoll" disposal
- */
- bool dying;
-
/* List containing poll wait queues */
struct eppoll_entry *pwqlist;
@@ -220,10 +213,7 @@ struct eventpoll {
struct hlist_head refs;
u8 loop_check_depth;
- /*
- * usage count, used together with epitem->dying to
- * orchestrate the disposal of this struct
- */
+ /* usage count, orchestrates "struct eventpoll" disposal */
refcount_t refcount;
/* used to defer freeing past ep_get_upwards_depth_proc() RCU walk */
@@ -918,13 +908,10 @@ static void ep_remove(struct eventpoll *ep, struct epitem *epi)
ep_unregister_pollwait(ep, epi);
- /* cheap sync with eventpoll_release_file() */
- if (unlikely(READ_ONCE(epi->dying)))
- return;
-
/*
* If we manage to grab a reference it means we're not in
- * eventpoll_release_file() and aren't going to be.
+ * eventpoll_release_file() and aren't going to be: once @file's
+ * refcount has reached zero, file_ref_get() cannot bring it back.
*/
file = epi_fget(epi);
if (!file)
@@ -1126,15 +1113,15 @@ void eventpoll_release_file(struct file *file)
struct epitem *epi;
/*
- * Use the 'dying' flag to prevent a concurrent ep_clear_and_put() from
- * touching the epitems list before eventpoll_release_file() can access
- * the ep->mtx.
+ * A concurrent ep_remove() cannot outrace us: it pins @file via
+ * epi_fget(), which fails once __fput() has dropped the refcount
+ * to zero -- the path we're on. So any racing ep_remove() bails
+ * and leaves the epi for us to clean up here.
*/
again:
spin_lock(&file->f_lock);
if (file->f_ep && file->f_ep->first) {
epi = hlist_entry(file->f_ep->first, struct epitem, fllink);
- WRITE_ONCE(epi->dying, true);
spin_unlock(&file->f_lock);
/*
--
2.50.1 (Apple Git-155)

View File

@ -0,0 +1,57 @@
From 2eb0a1e08ebad5be00433093449abf025450ca6d Mon Sep 17 00:00:00 2001
From: Ian Kent <ikent@redhat.com>
Date: Wed, 17 Jun 2026 12:08:43 +0800
Subject: [PATCH] eventpoll: Fix integer overflow in ep_loop_check_proc()
JIRA: https://redhat.atlassian.net/browse/RHEL-180777
Upstream status: Linus
commit fdcfce93073d990ed4b71752e31ad1c1d6e9d58b
Author: Jann Horn <jannh@google.com>
Date: Mon Feb 23 20:59:33 2026 +0100
eventpoll: Fix integer overflow in ep_loop_check_proc()
If a recursive call to ep_loop_check_proc() hits the `result = INT_MAX`,
an integer overflow will occur in the calling ep_loop_check_proc() at
`result = max(result, ep_loop_check_proc(ep_tovisit, depth + 1) + 1)`,
breaking the recursion depth check.
Fix it by using a different placeholder value that can't lead to an
overflow.
Reported-by: Guenter Roeck <linux@roeck-us.net>
Fixes: f2e467a48287 ("eventpoll: Fix semi-unbounded recursion")
Cc: stable@vger.kernel.org
Signed-off-by: Jann Horn <jannh@google.com>
Link: https://patch.msgid.link/20260223-epoll-int-overflow-v1-1-452f35132224@google.com
Signed-off-by: Christian Brauner <brauner@kernel.org>
Signed-off-by: Ian Kent <ikent@redhat.com>
diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index 5228ff9c8742..cb21dfe9ee0a 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -2130,7 +2130,8 @@ static int ep_poll(struct eventpoll *ep, struct epoll_event __user *events,
* @ep: the &struct eventpoll to be currently checked.
* @depth: Current depth of the path being checked.
*
- * Return: depth of the subtree, or INT_MAX if we found a loop or went too deep.
+ * Return: depth of the subtree, or a value bigger than EP_MAX_NESTS if we found
+ * a loop or went too deep.
*/
static int ep_loop_check_proc(struct eventpoll *ep, int depth)
{
@@ -2149,7 +2150,7 @@ static int ep_loop_check_proc(struct eventpoll *ep, int depth)
struct eventpoll *ep_tovisit;
ep_tovisit = epi->ffd.file->private_data;
if (ep_tovisit == inserting_into || depth > EP_MAX_NESTS)
- result = INT_MAX;
+ result = EP_MAX_NESTS+1;
else
result = max(result, ep_loop_check_proc(ep_tovisit, depth + 1) + 1);
if (result > EP_MAX_NESTS)
--
2.50.1 (Apple Git-155)

View File

@ -0,0 +1,101 @@
From 39c9ab147e5bc9cf20d146d78c59afc7a0ab488d Mon Sep 17 00:00:00 2001
From: Ian Kent <ikent@redhat.com>
Date: Thu, 18 Jun 2026 11:30:35 +0800
Subject: [PATCH] eventpoll: refresh epi_fget() / ep_remove_file() comments
JIRA: https://redhat.atlassian.net/browse/RHEL-180777
Upstream status: Linux
commit a573cb40f9819c3fe81a43eb10170f8fc8eddc5e
Author: Christian Brauner <brauner@kernel.org>
Date: Fri Apr 24 15:46:35 2026 +0200
eventpoll: refresh epi_fget() / ep_remove_file() comments
Two comments drifted from the code they sit on.
epi_fget()'s block comment still referenced atomic_long_inc_not_zero,
which has been file_ref_get() for a while, and described only one of
the function's two roles: safe dereference of epi->ffd.file under
ep->mtx. Since commit a6dc643c6931 ("eventpoll: fix ep_remove struct
eventpoll / struct file UAF") the refcount bump also serves as a pin
that blocks __fput() from starting, which is what lets ep_remove()
touch file->f_lock and file->f_ep without racing
eventpoll_release_file(). Update the block to name both roles and the
commit that introduced the pin role.
ep_remove_file()'s one-line "See eventpoll_release() for details"
pointed at an inline in include/linux/eventpoll.h but said nothing
about what those details were. Replace it with a short explanation:
we publish NULL so the eventpoll_release() fastpath can skip the slow
path, and this is safe because every f_ep writer either holds a pin
via epi_fget() or is __fput() itself.
Comment-only; no functional change.
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Link: https://patch.msgid.link/20260424-work-epoll-rework-v1-4-249ed00a20f3@kernel.org
Signed-off-by: Christian Brauner <brauner@kernel.org>
Signed-off-by: Ian Kent <ikent@redhat.com>
diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index cb21dfe9ee0a..9bde520ac74b 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -817,22 +817,23 @@ static void ep_free(struct eventpoll *ep)
}
/*
- * The ffd.file pointer may be in the process of being torn down due to
- * being closed, but we may not have finished eventpoll_release() yet.
+ * Pin @epi->ffd.file for operations that require both safe dereference
+ * and exclusion from __fput().
*
- * Normally, even with the atomic_long_inc_not_zero, the file may have
- * been free'd and then gotten re-allocated to something else (since
- * files are not RCU-delayed, they are SLAB_TYPESAFE_BY_RCU).
+ * struct file uses SLAB_TYPESAFE_BY_RCU, so a freed slot can be
+ * reassigned at any time. The bare load of epi->ffd.file is safe here
+ * because the caller holds ep->mtx and eventpoll_release_file() blocks
+ * on that mutex while tearing down the epi, so the backing file
+ * allocation cannot be freed and reused under us. An rcu_read_lock()
+ * is therefore unnecessary for the load.
*
- * But for epoll, users hold the ep->mtx mutex, and as such any file in
- * the process of being free'd will block in eventpoll_release_file()
- * and thus the underlying file allocation will not be free'd, and the
- * file re-use cannot happen.
- *
- * For the same reason we can avoid a rcu_read_lock() around the
- * operation - 'ffd.file' cannot go away even if the refcount has
- * reached zero (but we must still not call out to ->poll() functions
- * etc).
+ * A successful file_ref_get() additionally blocks __fput() from
+ * starting on this file: once the refcount has reached zero it cannot
+ * come back. ep_remove() relies on that to touch file->f_lock and
+ * file->f_ep without racing eventpoll_release_file() (see commit
+ * a6dc643c6931). A NULL return means __fput() is already in flight;
+ * the caller must bail without touching the file, and
+ * eventpoll_release_file() will clean the epi up from its side.
*/
static struct file *epi_fget(const struct epitem *epi)
{
@@ -858,7 +859,13 @@ static void ep_remove_file(struct eventpoll *ep, struct epitem *epi,
spin_lock(&file->f_lock);
head = file->f_ep;
if (hlist_is_singular_node(&epi->fllink, head)) {
- /* See eventpoll_release() for details. */
+ /*
+ * Last watcher: publish NULL so the eventpoll_release()
+ * fastpath in include/linux/eventpoll.h can skip the slow
+ * path on a future __fput(). Safe because every f_ep writer
+ * either holds a pin on @file via epi_fget() or is __fput()
+ * itself -- see the comment in eventpoll_release().
+ */
WRITE_ONCE(file->f_ep, NULL);
if (!is_file_epoll(file)) {
struct epitems_head *v;
--
2.50.1 (Apple Git-155)

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.4
%define pkgrelease 211.31.1
%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.4%{?buildid}%{?dist}
%define specrelease 211.31.1%{?buildid}%{?dist}
# This defines the kabi tarball version
%define kabiversion 6.12.0-211.7.1.el10_2
@ -1451,9 +1451,30 @@ 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
Patch1418: 1418-rtmutex-use-waiter-task-in-remove-waiter.patch
Patch1420: 1420-rtmutex-skip-remove-waiter-when-not-enqueued.patch
Patch1421: 1421-net-page-pool-avoid-false-positive-warning-if-napi-was-never.patch
Patch1422: 1422-net-mana-fix-double-destroy-workqueue-on-service-rescan-pci.patch
Patch1423: 1423-net-mana-null-service-wq-on-setup-error-to-prevent-double-de.patch
Patch1424: 1424-net-ipv6-ioam6-prevent-schema-length-wraparound-in-trace-fil.patch
Patch1425: 1425-procfs-fix-missing-rcu-protection-when-reading-real-parent-i.patch
Patch1426: 1426-tg3-fix-race-for-querying-speed-duplex.patch
Patch1427: 1427-smb-client-fix-off-by-8-bounds-check-in-check-wsl-eas.patch
Patch1428: 1428-smb-client-fix-out-of-bounds-read-in-smb2-compound-op.patch
Patch1429: 1429-epoll-annotate-racy-check.patch
Patch1430: 1430-eventpoll-defer-struct-eventpoll-free-to-rcu-grace-period.patch
Patch1431: 1431-eventpoll-use-hlist-is-singular-node-in-ep-remove.patch
Patch1432: 1432-eventpoll-split-ep-remove.patch
Patch1433: 1433-eventpoll-kill-ep-remove.patch
Patch1434: 1434-eventpoll-rename-ep-remove-safe-back-to-ep-remove.patch
Patch1435: 1435-eventpoll-move-epi-fget-up.patch
Patch1436: 1436-eventpoll-drop-vestigial-prefix-from-ep-remove-file-epi.patch
Patch1437: 1437-eventpoll-fix-ep-remove-struct-eventpoll-struct-file-uaf.patch
Patch1438: 1438-eventpoll-move-f-lock-acquisition-into-ep-remove-file.patch
Patch1439: 1439-eventpoll-refresh-eventpoll-release-fast-path-comment.patch
Patch1440: 1440-eventpoll-drop-dead-bool-return-from-ep-remove-epi.patch
Patch1441: 1441-eventpoll-fix-semi-unbounded-recursion.patch
Patch1442: 1442-eventpoll-drop-vestigial-epi-dying-flag.patch
Patch1443: 1443-eventpoll-fix-integer-overflow-in-ep-loop-check-proc.patch
Patch1444: 1444-eventpoll-refresh-epi-fget-ep-remove-file-comments.patch
# END OF PATCH DEFINITIONS
%description
@ -2627,9 +2648,30 @@ 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
ApplyPatch 1418-rtmutex-use-waiter-task-in-remove-waiter.patch
ApplyPatch 1420-rtmutex-skip-remove-waiter-when-not-enqueued.patch
ApplyPatch 1421-net-page-pool-avoid-false-positive-warning-if-napi-was-never.patch
ApplyPatch 1422-net-mana-fix-double-destroy-workqueue-on-service-rescan-pci.patch
ApplyPatch 1423-net-mana-null-service-wq-on-setup-error-to-prevent-double-de.patch
ApplyPatch 1424-net-ipv6-ioam6-prevent-schema-length-wraparound-in-trace-fil.patch
ApplyPatch 1425-procfs-fix-missing-rcu-protection-when-reading-real-parent-i.patch
ApplyPatch 1426-tg3-fix-race-for-querying-speed-duplex.patch
ApplyPatch 1427-smb-client-fix-off-by-8-bounds-check-in-check-wsl-eas.patch
ApplyPatch 1428-smb-client-fix-out-of-bounds-read-in-smb2-compound-op.patch
ApplyPatch 1429-epoll-annotate-racy-check.patch
ApplyPatch 1430-eventpoll-defer-struct-eventpoll-free-to-rcu-grace-period.patch
ApplyPatch 1431-eventpoll-use-hlist-is-singular-node-in-ep-remove.patch
ApplyPatch 1432-eventpoll-split-ep-remove.patch
ApplyPatch 1433-eventpoll-kill-ep-remove.patch
ApplyPatch 1434-eventpoll-rename-ep-remove-safe-back-to-ep-remove.patch
ApplyPatch 1435-eventpoll-move-epi-fget-up.patch
ApplyPatch 1436-eventpoll-drop-vestigial-prefix-from-ep-remove-file-epi.patch
ApplyPatch 1437-eventpoll-fix-ep-remove-struct-eventpoll-struct-file-uaf.patch
ApplyPatch 1438-eventpoll-move-f-lock-acquisition-into-ep-remove-file.patch
ApplyPatch 1439-eventpoll-refresh-eventpoll-release-fast-path-comment.patch
ApplyPatch 1440-eventpoll-drop-dead-bool-return-from-ep-remove-epi.patch
ApplyPatch 1441-eventpoll-fix-semi-unbounded-recursion.patch
ApplyPatch 1442-eventpoll-drop-vestigial-epi-dying-flag.patch
ApplyPatch 1443-eventpoll-fix-integer-overflow-in-ep-loop-check-proc.patch
ApplyPatch 1444-eventpoll-refresh-epi-fget-ep-remove-file-comments.patch
# END OF PATCH APPLICATIONS
# Any further pre-build tree manipulations happen here.
@ -5134,6 +5176,39 @@ fi\
#
#
%changelog
* Wed Jul 08 2026 Andrew Lukoshko <alukoshko@almalinux.org> - 6.12.0-211.31.1
- Recreate RHEL 6.12.0-211.31.1 from CentOS Stream 10 and upstream stable backports (1421-1444)
- The AlmaLinux ahead-of-RHEL eventpoll CVE-2026-46242 fix (1417) is superseded by
RHEL's eventpoll series carrying the same fix and is dropped
- Temporarily drop the rtmutex remove_waiter() fixes (1418, 1420)
- RHEL changelog for 211.31.1 follows:
* Mon Jul 06 2026 CKI KWF Bot <cki-ci-bot+kwf-gitlab-com@redhat.com> [6.12.0-211.31.1.el10_2]
- eventpoll: refresh epi_fget() / ep_remove_file() comments (Ian Kent) [RHEL-180776]
- eventpoll: Fix integer overflow in ep_loop_check_proc() (Ian Kent) [RHEL-180776]
- eventpoll: drop vestigial epi->dying flag (Ian Kent) [RHEL-180776]
- eventpoll: Fix semi-unbounded recursion (Ian Kent) [RHEL-180776] {CVE-2025-38614}
- eventpoll: drop dead bool return from ep_remove_epi() (Ian Kent) [RHEL-180776]
- eventpoll: refresh eventpoll_release() fast-path comment (Ian Kent) [RHEL-180776]
- eventpoll: move f_lock acquisition into ep_remove_file() (Ian Kent) [RHEL-180776]
- eventpoll: fix ep_remove struct eventpoll / struct file UAF (Ian Kent) [RHEL-180776] {CVE-2026-46242}
- eventpoll: drop vestigial __ prefix from ep_remove_{file,epi}() (Ian Kent) [RHEL-180776]
- eventpoll: move epi_fget() up (Ian Kent) [RHEL-180776]
- eventpoll: rename ep_remove_safe() back to ep_remove() (Ian Kent) [RHEL-180776]
- eventpoll: kill __ep_remove() (Ian Kent) [RHEL-180776]
- eventpoll: split __ep_remove() (Ian Kent) [RHEL-180776]
- eventpoll: use hlist_is_singular_node() in __ep_remove() (Ian Kent) [RHEL-180776]
- eventpoll: defer struct eventpoll free to RCU grace period (Ian Kent) [RHEL-173832] {CVE-2026-43074}
- epoll: annotate racy check (Ian Kent) [RHEL-180776]
- smb/client: fix out-of-bounds read in smb2_compound_op() (Paulo Alcantara) [RHEL-180048] {CVE-2026-46155}
- smb: client: fix off-by-8 bounds check in check_wsl_eas() (Paulo Alcantara) [RHEL-180048]
- tg3: Fix race for querying speed/duplex (CKI Backport Bot) [RHEL-182770]
- procfs: fix missing RCU protection when reading real_parent in do_task_stat() (CKI Backport Bot) [RHEL-181902] {CVE-2026-46259}
- net/ipv6: ioam6: prevent schema length wraparound in trace fill (Antoine Tenart) [RHEL-174786] {CVE-2026-43341}
- net/mana: Null service_wq on setup error to prevent double destroy (CKI Backport Bot) [RHEL-180277] {CVE-2026-43276}
- net: mana: Fix double destroy_workqueue on service rescan PCI path (CKI Backport Bot) [RHEL-180277] {CVE-2026-43276}
- net: page_pool: avoid false positive warning if NAPI was never added (Ivan Vecera) [RHEL-162140]
* Tue Jul 08 2026 Andrew Lukoshko <alukoshko@almalinux.org> - 6.12.0-211.30.4
- Fix rtmutex self-deadlock NULL pointer dereference in remove_waiter() ahead of
RHEL, upstream 3bfdc63936dd + 40a25d59e85b (1418, 1420); the futex_requeue guard