Skip to content

[rocky10_2] History Rebuild through kernel-6.12.0-211.32.1.el10_2#1430

Open
PlaidCat wants to merge 29 commits into
rocky10_2from
rocky10_2_rebuild
Open

[rocky10_2] History Rebuild through kernel-6.12.0-211.32.1.el10_2#1430
PlaidCat wants to merge 29 commits into
rocky10_2from
rocky10_2_rebuild

Conversation

@PlaidCat

@PlaidCat PlaidCat commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

This is an automated kernel history rebuild using cron and internal tooling. It follows the same process used for previous history rebuilds:

  • Download all unprocessed src.rpm packages
  • For each src.rpm:
    • Identify all commits in the changelog up to the last known tag (6.12.0-211)
    • Replay commits in chronological order (oldest to newest in the changelog) using git cherry-pick
    • Replace the code in the branch with the output of rpmbuild -bp for the corresponding src.rpm
    • Tag the rebuild branch

JIRA Tickets

Rebuild Splat Inspection

kernel-6.12.0-211.32.1.el10_2

$ cat ciq/ciq_backports/kernel-6.12.0-211.32.1.el10_2/rebuild.details.txt
Rebuild_History BUILDABLE
Rebuilding Kernel from rpm changelog with Fuzz Limit: 87.50%
Number of commits in upstream range v6.12~1..kernel-mainline: 138299
Number of commits in rpm: 30
Number of commits matched with upstream: 28 (93.33%)
Number of commits in upstream but not in rpm: 138271
Number of commits NOT found in upstream: 2 (6.67%)

Rebuilding Kernel on Branch rocky10_2_rebuild_kernel-6.12.0-211.32.1.el10_2 for kernel-6.12.0-211.32.1.el10_2
Clean Cherry Picks: 28 (100.00%)
Empty Cherry Picks: 0 (0.00%)
_______________________________

__EMPTY COMMITS__________________________

__CHANGES NOT IN UPSTREAM________________
Add partial riscv64 support for build root'
Provide basic VisionFive 2 support'

BUILD

$ grep -E -B 5 -A 5 "\[TIMER\]|^Starting Build" $(ls -t kbuild* | head -n1)
/mnt/code/kernel-src-tree-build
Running make mrproper...
  CLEAN   scripts/basic
  CLEAN   scripts/kconfig
  CLEAN   include/config include/generated
[TIMER]{MRPROPER}: 6s
x86_64 architecture detected, copying config
'configs/kernel-x86_64-rhel.config' -> '.config'
Setting Local Version for build
CONFIG_LOCALVERSION="-rocky10_2_rebuild-f1dd39adef4e"
Making olddefconfig
--
  HOSTCC  scripts/kconfig/util.o
  HOSTLD  scripts/kconfig/conf
#
# configuration written to .config
#
Starting Build
  GEN     arch/x86/include/generated/asm/orc_hash.h
  WRAP    arch/x86/include/generated/uapi/asm/bpf_perf_event.h
  WRAP    arch/x86/include/generated/uapi/asm/errno.h
  WRAP    arch/x86/include/generated/uapi/asm/fcntl.h
  WRAP    arch/x86/include/generated/uapi/asm/ioctl.h
--
  LD [M]  net/qrtr/qrtr-mhi.ko
  LD [M]  virt/lib/irqbypass.ko
  BTF [M] net/qrtr/qrtr.ko
  BTF [M] net/qrtr/qrtr-mhi.ko
  BTF [M] virt/lib/irqbypass.ko
[TIMER]{BUILD}: 2312s
Making Modules
  SYMLINK /lib/modules/6.12.0-rocky10_2_rebuild-f1dd39adef4e+/build
  INSTALL /lib/modules/6.12.0-rocky10_2_rebuild-f1dd39adef4e+/modules.order
  INSTALL /lib/modules/6.12.0-rocky10_2_rebuild-f1dd39adef4e+/modules.builtin
  INSTALL /lib/modules/6.12.0-rocky10_2_rebuild-f1dd39adef4e+/modules.builtin.modinfo
--
  SIGN    /lib/modules/6.12.0-rocky10_2_rebuild-f1dd39adef4e+/kernel/net/qrtr/qrtr.ko
  STRIP   /lib/modules/6.12.0-rocky10_2_rebuild-f1dd39adef4e+/kernel/virt/lib/irqbypass.ko
  SIGN    /lib/modules/6.12.0-rocky10_2_rebuild-f1dd39adef4e+/kernel/net/qrtr/qrtr-mhi.ko
  SIGN    /lib/modules/6.12.0-rocky10_2_rebuild-f1dd39adef4e+/kernel/virt/lib/irqbypass.ko
  DEPMOD  /lib/modules/6.12.0-rocky10_2_rebuild-f1dd39adef4e+
[TIMER]{MODULES}: 11s
Making Install
  INSTALL /boot
[TIMER]{INSTALL}: 17s
Checking kABI
kABI check passed
Setting Default Kernel to /boot/vmlinuz-6.12.0-rocky10_2_rebuild-f1dd39adef4e+ and Index to 2
Hopefully Grub2.0 took everything ... rebooting after time metrices
[TIMER]{MRPROPER}: 6s
[TIMER]{BUILD}: 2312s
[TIMER]{MODULES}: 11s
[TIMER]{INSTALL}: 17s
[TIMER]{TOTAL} 2351s
Rebooting in 10 seconds

KSelfTests

$ get_kselftest_diff.sh
kselftest.6.12.0-rocky10_2_rebuild-9b071f21cf07+.log
492
kselftest.6.12.0-rocky10_2_rebuild-4b56adaf67e7+.log
492
kselftest.6.12.0-rocky10_2_rebuild-64e1e61972af+.log
492
kselftest.6.12.0-rocky10_2_rebuild-f1dd39adef4e+.log
491
Before: kselftest.6.12.0-rocky10_2_rebuild-64e1e61972af+.log
After: kselftest.6.12.0-rocky10_2_rebuild-f1dd39adef4e+.log
Diff:
-ok 2 selftests: seccomp: seccomp_benchmark
-ok 7 selftests: timers: raw_skew # SKIP
+ok 7 selftests: timers: raw_skew

PlaidCat added 29 commits July 10, 2026 16:28
jira KERNEL-1285
Rebuild_History Non-Buildable kernel-6.12.0-211.32.1.el10_2
commit-author Jakub Kicinski <kuba@kernel.org>
commit c1e00bc

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>
(cherry picked from commit c1e00bc)
	Signed-off-by: Jonathan Maple <jmaple@ciq.com>
jira KERNEL-1285
cve CVE-2026-43276
Rebuild_History Non-Buildable kernel-6.12.0-211.32.1.el10_2
commit-author Dipayaan Roy <dipayanroy@linux.microsoft.com>
commit f975a09

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: 505cc26 ("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>
(cherry picked from commit f975a09)
	Signed-off-by: Jonathan Maple <jmaple@ciq.com>
jira KERNEL-1285
cve CVE-2026-43276
Rebuild_History Non-Buildable kernel-6.12.0-211.32.1.el10_2
commit-author Shiraz Saleem <shirazsaleem@microsoft.com>
commit 87c2302

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: f975a09 ("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>
(cherry picked from commit 87c2302)
	Signed-off-by: Jonathan Maple <jmaple@ciq.com>
jira KERNEL-1285
cve CVE-2026-43341
Rebuild_History Non-Buildable kernel-6.12.0-211.32.1.el10_2
commit-author Pengpeng Hou <pengpeng@iscas.ac.cn>
commit 5e67ba9

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: 8c6f6fa ("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>
(cherry picked from commit 5e67ba9)
	Signed-off-by: Jonathan Maple <jmaple@ciq.com>
…k_stat()

jira KERNEL-1285
cve CVE-2026-46259
Rebuild_History Non-Buildable kernel-6.12.0-211.32.1.el10_2
commit-author Jinliang Zheng <alexjlzheng@tencent.com>
commit 76149d5

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: 06fffb1 ("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>
(cherry picked from commit 76149d5)
	Signed-off-by: Jonathan Maple <jmaple@ciq.com>
jira KERNEL-1285
Rebuild_History Non-Buildable kernel-6.12.0-211.32.1.el10_2
commit-author Thomas Bogendoerfer <tbogendoerfer@suse.de>
commit bb41745

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: 84421b9 ("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>
(cherry picked from commit bb41745)
	Signed-off-by: Jonathan Maple <jmaple@ciq.com>
jira KERNEL-1285
Rebuild_History Non-Buildable kernel-6.12.0-211.32.1.el10_2
commit-author Greg Kroah-Hartman <gregkh@linuxfoundation.org>
commit 3d8b9d0

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>
(cherry picked from commit 3d8b9d0)
	Signed-off-by: Jonathan Maple <jmaple@ciq.com>
jira KERNEL-1285
cve CVE-2026-46155
Rebuild_History Non-Buildable kernel-6.12.0-211.32.1.el10_2
commit-author Zisen Ye <zisenye@stu.xidian.edu.cn>
commit 8d09328

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: ea41367 ("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>
(cherry picked from commit 8d09328)
	Signed-off-by: Jonathan Maple <jmaple@ciq.com>
jira KERNEL-1285
Rebuild_History Non-Buildable kernel-6.12.0-211.32.1.el10_2
commit-author Christian Brauner <brauner@kernel.org>
commit 6474353

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>
(cherry picked from commit 6474353)
	Signed-off-by: Jonathan Maple <jmaple@ciq.com>
jira KERNEL-1285
cve CVE-2026-43074
Rebuild_History Non-Buildable kernel-6.12.0-211.32.1.el10_2
commit-author Nicholas Carlini <nicholas@carlini.com>
commit 07712db

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: f2e467a ("eventpoll: Fix semi-unbounded recursion")
	Signed-off-by: Nicholas Carlini <nicholas@carlini.com>
	Signed-off-by: Christian Brauner <brauner@kernel.org>
(cherry picked from commit 07712db)
	Signed-off-by: Jonathan Maple <jmaple@ciq.com>
jira KERNEL-1285
Rebuild_History Non-Buildable kernel-6.12.0-211.32.1.el10_2
commit-author Christian Brauner <brauner@kernel.org>
commit 3d9fd0a

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>
(cherry picked from commit 3d9fd0a)
	Signed-off-by: Jonathan Maple <jmaple@ciq.com>
jira KERNEL-1285
Rebuild_History Non-Buildable kernel-6.12.0-211.32.1.el10_2
commit-author Christian Brauner <brauner@kernel.org>
commit 0f7bdfd

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>
(cherry picked from commit 0f7bdfd)
	Signed-off-by: Jonathan Maple <jmaple@ciq.com>
jira KERNEL-1285
Rebuild_History Non-Buildable kernel-6.12.0-211.32.1.el10_2
commit-author Christian Brauner <brauner@kernel.org>
commit e9e5cd4

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>
(cherry picked from commit e9e5cd4)
	Signed-off-by: Jonathan Maple <jmaple@ciq.com>
jira KERNEL-1285
Rebuild_History Non-Buildable kernel-6.12.0-211.32.1.el10_2
commit-author Christian Brauner <brauner@kernel.org>
commit 0bade23

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>
(cherry picked from commit 0bade23)
	Signed-off-by: Jonathan Maple <jmaple@ciq.com>
jira KERNEL-1285
Rebuild_History Non-Buildable kernel-6.12.0-211.32.1.el10_2
commit-author Christian Brauner <brauner@kernel.org>
commit 86e8705

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>
(cherry picked from commit 86e8705)
	Signed-off-by: Jonathan Maple <jmaple@ciq.com>
jira KERNEL-1285
Rebuild_History Non-Buildable kernel-6.12.0-211.32.1.el10_2
commit-author Christian Brauner <brauner@kernel.org>
commit 0feaf64

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>
(cherry picked from commit 0feaf64)
	Signed-off-by: Jonathan Maple <jmaple@ciq.com>
jira KERNEL-1285
cve CVE-2026-46242
Rebuild_History Non-Buildable kernel-6.12.0-211.32.1.el10_2
commit-author Christian Brauner <brauner@kernel.org>
commit a6dc643

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: 58c9b01 ("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>
(cherry picked from commit a6dc643)
	Signed-off-by: Jonathan Maple <jmaple@ciq.com>
jira KERNEL-1285
Rebuild_History Non-Buildable kernel-6.12.0-211.32.1.el10_2
commit-author Christian Brauner <brauner@kernel.org>
commit d30deeb

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>
(cherry picked from commit d30deeb)
	Signed-off-by: Jonathan Maple <jmaple@ciq.com>
jira KERNEL-1285
Rebuild_History Non-Buildable kernel-6.12.0-211.32.1.el10_2
commit-author Christian Brauner <brauner@kernel.org>
commit 33e92e9

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>
(cherry picked from commit 33e92e9)
	Signed-off-by: Jonathan Maple <jmaple@ciq.com>
jira KERNEL-1285
Rebuild_History Non-Buildable kernel-6.12.0-211.32.1.el10_2
commit-author Christian Brauner <brauner@kernel.org>
commit 3a4551e

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>
(cherry picked from commit 3a4551e)
	Signed-off-by: Jonathan Maple <jmaple@ciq.com>
jira KERNEL-1285
cve CVE-2025-38614
Rebuild_History Non-Buildable kernel-6.12.0-211.32.1.el10_2
commit-author Jann Horn <jannh@google.com>
commit f2e467a

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
   28d82dc ("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 8c44dac ("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: 22bacca ("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>
(cherry picked from commit f2e467a)
	Signed-off-by: Jonathan Maple <jmaple@ciq.com>
jira KERNEL-1285
Rebuild_History Non-Buildable kernel-6.12.0-211.32.1.el10_2
commit-author Christian Brauner <brauner@kernel.org>
commit 07422c9

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>
(cherry picked from commit 07422c9)
	Signed-off-by: Jonathan Maple <jmaple@ciq.com>
jira KERNEL-1285
Rebuild_History Non-Buildable kernel-6.12.0-211.32.1.el10_2
commit-author Jann Horn <jannh@google.com>
commit fdcfce9

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: f2e467a ("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>
(cherry picked from commit fdcfce9)
	Signed-off-by: Jonathan Maple <jmaple@ciq.com>
jira KERNEL-1285
Rebuild_History Non-Buildable kernel-6.12.0-211.32.1.el10_2
commit-author Christian Brauner <brauner@kernel.org>
commit a573cb4

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 a6dc643 ("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>
(cherry picked from commit a573cb4)
	Signed-off-by: Jonathan Maple <jmaple@ciq.com>
…DALL

jira KERNEL-1285
cve CVE-2026-46227
Rebuild_History Non-Buildable kernel-6.12.0-211.32.1.el10_2
commit-author Ben Morris <bmorris@anthropic.com>
commit abb5f36

The SCTP_SENDALL path in sctp_sendmsg() iterates ep->asocs with
list_for_each_entry_safe(), which caches the next entry in @tmp before
the loop body runs.  The body calls sctp_sendmsg_to_asoc(), which may
drop the socket lock inside sctp_wait_for_sndbuf().

While the lock is dropped, another thread can SCTP_SOCKOPT_PEELOFF the
association cached in @tmp, migrating it to a new endpoint via
sctp_sock_migrate() (list_del_init() + list_add_tail() to
newep->asocs), and optionally close the new socket which frees the
association via kfree_rcu().  The cached @tmp can also be freed by a
network ABORT for that association, processed in softirq while the
lock is dropped.

sctp_wait_for_sndbuf() revalidates @asoc (the current entry) on re-lock
via the "sk != asoc->base.sk" and "asoc->base.dead" checks, but nothing
revalidates @tmp.  After a successful return, the iterator advances to
the stale @tmp, yielding either a use-after-free (if the peeled socket
was closed) or a list-walk onto the new endpoint's list head (type
confusion of &newep->asocs as a struct sctp_association *).

Both are reachable from CapEff=0; the type-confusion path gives
controlled indirect call via the outqueue.sched->init_sid pointer.

Fix by re-deriving @tmp from @asoc after sctp_sendmsg_to_asoc()
returns.  @asoc is known to still be on ep->asocs at that point: the
only callers that list_del an association from ep->asocs are
sctp_association_free() (which sets asoc->base.dead) and
sctp_assoc_migrate() (which changes asoc->base.sk), and
sctp_wait_for_sndbuf() checks both under the lock before any
successful return; a tripped check propagates as err < 0 and the loop
bails before the re-derive.

The SCTP_ABORT path in sctp_sendmsg_check_sflags() returns 0 and the
loop hits 'continue' before sctp_sendmsg_to_asoc() is ever called, so
the @tmp cached by list_for_each_entry_safe() still covers the
lock-held free that ba59fb0 ("sctp: walk the list of asoc
safely") was added for.

Fixes: 4910280 ("sctp: add support for snd flag SCTP_SENDALL process in sendmsg")
	Cc: stable@vger.kernel.org
	Signed-off-by: Ben Morris <bmorris@anthropic.com>
	Acked-by: Xin Long <lucien.xin@gmail.com>
Link: https://patch.msgid.link/20260508001455.3137-1-joycathacker@gmail.com
	Signed-off-by: Jakub Kicinski <kuba@kernel.org>
(cherry picked from commit abb5f36)
	Signed-off-by: Jonathan Maple <jmaple@ciq.com>
…in ets_qdisc_change

jira KERNEL-1285
cve CVE-2025-71066
Rebuild_History Non-Buildable kernel-6.12.0-211.32.1.el10_2
commit-author Jamal Hadi Salim <jhs@mojatatu.com>
commit ce052b9

zdi-disclosures@trendmicro.com says:

The vulnerability is a race condition between `ets_qdisc_dequeue` and
`ets_qdisc_change`.  It leads to UAF on `struct Qdisc` object.
Attacker requires the capability to create new user and network namespace
in order to trigger the bug.
See my additional commentary at the end of the analysis.

Analysis:

static int ets_qdisc_change(struct Qdisc *sch, struct nlattr *opt,
                          struct netlink_ext_ack *extack)
{
...

      // (1) this lock is preventing .change handler (`ets_qdisc_change`)
      //to race with .dequeue handler (`ets_qdisc_dequeue`)
      sch_tree_lock(sch);

      for (i = nbands; i < oldbands; i++) {
              if (i >= q->nstrict && q->classes[i].qdisc->q.qlen)
                      list_del_init(&q->classes[i].alist);
              qdisc_purge_queue(q->classes[i].qdisc);
      }

      WRITE_ONCE(q->nbands, nbands);
      for (i = nstrict; i < q->nstrict; i++) {
              if (q->classes[i].qdisc->q.qlen) {
		      // (2) the class is added to the q->active
                      list_add_tail(&q->classes[i].alist, &q->active);
                      q->classes[i].deficit = quanta[i];
              }
      }
      WRITE_ONCE(q->nstrict, nstrict);
      memcpy(q->prio2band, priomap, sizeof(priomap));

      for (i = 0; i < q->nbands; i++)
              WRITE_ONCE(q->classes[i].quantum, quanta[i]);

      for (i = oldbands; i < q->nbands; i++) {
              q->classes[i].qdisc = queues[i];
              if (q->classes[i].qdisc != &noop_qdisc)
                      qdisc_hash_add(q->classes[i].qdisc, true);
      }

      // (3) the qdisc is unlocked, now dequeue can be called in parallel
      // to the rest of .change handler
      sch_tree_unlock(sch);

      ets_offload_change(sch);
      for (i = q->nbands; i < oldbands; i++) {
	      // (4) we're reducing the refcount for our class's qdisc and
	      //  freeing it
              qdisc_put(q->classes[i].qdisc);
	      // (5) If we call .dequeue between (4) and (5), we will have
	      // a strong UAF and we can control RIP
              q->classes[i].qdisc = NULL;
              WRITE_ONCE(q->classes[i].quantum, 0);
              q->classes[i].deficit = 0;
              gnet_stats_basic_sync_init(&q->classes[i].bstats);
              memset(&q->classes[i].qstats, 0, sizeof(q->classes[i].qstats));
      }
      return 0;
}

Comment:
This happens because some of the classes have their qdiscs assigned to
NULL, but remain in the active list. This commit fixes this issue by always
removing the class from the active list before deleting and freeing its
associated qdisc

Reproducer Steps
(trimmed version of what was sent by zdi-disclosures@trendmicro.com)

```
DEV="${DEV:-lo}"
ROOT_HANDLE="${ROOT_HANDLE:-1:}"
BAND2_HANDLE="${BAND2_HANDLE:-20:}"   # child under 1:2
PING_BYTES="${PING_BYTES:-48}"
PING_COUNT="${PING_COUNT:-200000}"
PING_DST="${PING_DST:-127.0.0.1}"

SLOW_TBF_RATE="${SLOW_TBF_RATE:-8bit}"
SLOW_TBF_BURST="${SLOW_TBF_BURST:-100b}"
SLOW_TBF_LAT="${SLOW_TBF_LAT:-1s}"

cleanup() {
  tc qdisc del dev "$DEV" root 2>/dev/null
}
trap cleanup EXIT

ip link set "$DEV" up

tc qdisc del dev "$DEV" root 2>/dev/null || true

tc qdisc add dev "$DEV" root handle "$ROOT_HANDLE" ets bands 2 strict 2

tc qdisc add dev "$DEV" parent 1:2 handle "$BAND2_HANDLE" \
  tbf rate "$SLOW_TBF_RATE" burst "$SLOW_TBF_BURST" latency "$SLOW_TBF_LAT"

tc filter add dev "$DEV" parent 1: protocol all prio 1 u32 match u32 0 0 flowid 1:2
tc -s qdisc ls dev $DEV

ping -I "$DEV" -f -c "$PING_COUNT" -s "$PING_BYTES" -W 0.001 "$PING_DST" \
  >/dev/null 2>&1 &
tc qdisc change dev "$DEV" root handle "$ROOT_HANDLE" ets bands 2 strict 0
tc qdisc change dev "$DEV" root handle "$ROOT_HANDLE" ets bands 2 strict 2
tc -s qdisc ls dev $DEV
tc qdisc del dev "$DEV" parent 1:2 || true
tc -s qdisc ls dev $DEV
tc qdisc change dev "$DEV" root handle "$ROOT_HANDLE" ets bands 1 strict 1
```

KASAN report
```
==================================================================
BUG: KASAN: slab-use-after-free in ets_qdisc_dequeue+0x1071/0x11b0 kernel/net/sched/sch_ets.c:481
Read of size 8 at addr ffff8880502fc018 by task ping/12308
>
CPU: 0 UID: 0 PID: 12308 Comm: ping Not tainted 6.18.0-rc4-dirty #1 PREEMPT(full)
Hardware name: QEMU Ubuntu 25.04 PC (i440FX + PIIX, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
Call Trace:
 <IRQ>
 __dump_stack kernel/lib/dump_stack.c:94
 dump_stack_lvl+0x100/0x190 kernel/lib/dump_stack.c:120
 print_address_description kernel/mm/kasan/report.c:378
 print_report+0x156/0x4c9 kernel/mm/kasan/report.c:482
 kasan_report+0xdf/0x110 kernel/mm/kasan/report.c:595
 ets_qdisc_dequeue+0x1071/0x11b0 kernel/net/sched/sch_ets.c:481
 dequeue_skb kernel/net/sched/sch_generic.c:294
 qdisc_restart kernel/net/sched/sch_generic.c:399
 __qdisc_run+0x1c9/0x1b00 kernel/net/sched/sch_generic.c:417
 __dev_xmit_skb kernel/net/core/dev.c:4221
 __dev_queue_xmit+0x2848/0x4410 kernel/net/core/dev.c:4729
 dev_queue_xmit kernel/./include/linux/netdevice.h:3365
[...]

Allocated by task 17115:
 kasan_save_stack+0x30/0x50 kernel/mm/kasan/common.c:56
 kasan_save_track+0x14/0x30 kernel/mm/kasan/common.c:77
 poison_kmalloc_redzone kernel/mm/kasan/common.c:400
 __kasan_kmalloc+0xaa/0xb0 kernel/mm/kasan/common.c:417
 kasan_kmalloc kernel/./include/linux/kasan.h:262
 __do_kmalloc_node kernel/mm/slub.c:5642
 __kmalloc_node_noprof+0x34e/0x990 kernel/mm/slub.c:5648
 kmalloc_node_noprof kernel/./include/linux/slab.h:987
 qdisc_alloc+0xb8/0xc30 kernel/net/sched/sch_generic.c:950
 qdisc_create_dflt+0x93/0x490 kernel/net/sched/sch_generic.c:1012
 ets_class_graft+0x4fd/0x800 kernel/net/sched/sch_ets.c:261
 qdisc_graft+0x3e4/0x1780 kernel/net/sched/sch_api.c:1196
[...]

Freed by task 9905:
 kasan_save_stack+0x30/0x50 kernel/mm/kasan/common.c:56
 kasan_save_track+0x14/0x30 kernel/mm/kasan/common.c:77
 __kasan_save_free_info+0x3b/0x70 kernel/mm/kasan/generic.c:587
 kasan_save_free_info kernel/mm/kasan/kasan.h:406
 poison_slab_object kernel/mm/kasan/common.c:252
 __kasan_slab_free+0x5f/0x80 kernel/mm/kasan/common.c:284
 kasan_slab_free kernel/./include/linux/kasan.h:234
 slab_free_hook kernel/mm/slub.c:2539
 slab_free kernel/mm/slub.c:6630
 kfree+0x144/0x700 kernel/mm/slub.c:6837
 rcu_do_batch kernel/kernel/rcu/tree.c:2605
 rcu_core+0x7c0/0x1500 kernel/kernel/rcu/tree.c:2861
 handle_softirqs+0x1ea/0x8a0 kernel/kernel/softirq.c:622
 __do_softirq kernel/kernel/softirq.c:656
[...]

Commentary:

1. Maher Azzouzi working with Trend Micro Zero Day Initiative was reported as
the person who found the issue. I requested to get a proper email to add to the
reported-by tag but got no response. For this reason i will credit the person
i exchanged emails with i.e zdi-disclosures@trendmicro.com

2. Neither i nor Victor who did a much more thorough testing was able to
reproduce a UAF with the PoC or other approaches we tried. We were both able to
reproduce a null ptr deref. After exchange with zdi-disclosures@trendmicro.com
they sent a small change to be made to the code to add an extra delay which
was able to simulate the UAF. i.e, this:
   qdisc_put(q->classes[i].qdisc);
   mdelay(90);
   q->classes[i].qdisc = NULL;

I was informed by Thomas Gleixner(tglx@linutronix.de) that adding delays was
acceptable approach for demonstrating the bug, quote:
"Adding such delays is common exploit validation practice"
The equivalent delay could happen "by virt scheduling the vCPU out, SMIs,
NMIs, PREEMPT_RT enabled kernel"

3. I asked the OP to test and report back but got no response and after a
few days gave up and proceeded to submit this fix.

Fixes: de6d259 ("net/sched: sch_ets: don't peek at classes beyond 'nbands'")
	Reported-by: zdi-disclosures@trendmicro.com
	Tested-by: Victor Nogueira <victor@mojatatu.com>
	Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com>
	Reviewed-by: Davide Caratti <dcaratti@redhat.com>
Link: https://patch.msgid.link/20251128151919.576920-1-jhs@mojatatu.com
	Signed-off-by: Paolo Abeni <pabeni@redhat.com>

(cherry picked from commit ce052b9)
	Signed-off-by: Jonathan Maple <jmaple@ciq.com>
jira KERNEL-1285
cve CVE-2026-46113
Rebuild_History Non-Buildable kernel-6.12.0-211.32.1.el10_2
commit-author Sean Christopherson <seanjc@google.com>
commit 0cb2af2

The shadow MMU computes GFNs for direct shadow pages using sp->gfn plus
the SPTE index. This assumption breaks for shadow paging if the guest
page tables are modified between VM entries (similar to commit
aad885e, "KVM: x86/mmu: Drop/zap existing present SPTE even
when creating an MMIO SPTE", 2026-03-27).  The flow is as follows:

- a PDE is installed for a 2MB mapping, and a page in that area is
  accessed.  KVM creates a kvm_mmu_page consisting of 512 4KB pages;
  the kvm_mmu_page is marked by FNAME(fetch) as direct-mapped because
  the guest's mapping is a huge page (and thus contiguous).

- the PDE mapping is changed from outside the guest.

- the guest accesses another page in the same 2MB area.  KVM installs
  a new leaf SPTE and rmap entry; the SPTE uses the "correct" GFN
  (i.e. based on the new mapping, as changed in the previous step) but
  that GFN is outside of the [sp->gfn, sp->gfn + 511] range; therefore
  the rmap entry cannot be found and removed when the kvm_mmu_page
  is zapped.

- the memslot that covers the first 2MB mapping is deleted, and the
  kvm_mmu_page for the now-invalid GPA is zapped.  However, rmap_remove()
  only looks at the [sp->gfn, sp->gfn + 511] range established in step 1,
  and fails to find the rmap entry that was recorded by step 3.

- any operation that causes an rmap walk for the same page accessed
  by step 3 then walks a stale rmap and dereferences a freed kvm_mmu_page.
  This includes dirty logging or MMU notifier invalidations (e.g., from
  MADV_DONTNEED).

The underlying issue is that KVM's walking of shadow PTEs assumes that
if a SPTE is present when KVM wants to install a non-leaf SPTE, then the
existing kvm_mmu_page must be for the correct gfn.  Because the only way
for the gfn to be wrong is if KVM messed up and failed to zap a SPTE...
which shouldn't happen, but *actually* only happens in response to a
guest write.

That bug dates back literally forever, as even the first version of KVM
assumes that the GFN matches and walks into the "wrong" shadow page.
However, that was only an imprecision until 2032a93 ("KVM: MMU:
Don't allocate gfns page for direct mmu pages") came along.

Fix it by checking for a target gfn mismatch and zapping the existing
SPTE.  That way the old SP and rmap entries are gone, KVM installs
the rmap in the right location, and everyone is happy.

Fixes: 2032a93 ("KVM: MMU: Don't allocate gfns page for direct mmu pages")
Fixes: 6aa8b73 ("kvm: userspace interface")
	Reported-by: Alexander Bulekov <bkov@amazon.com>
	Reported-by: Fred Griffoul <fgriffo@amazon.co.uk>
	Cc: stable@vger.kernel.org
	Signed-off-by: Sean Christopherson <seanjc@google.com>
Link: https://patch.msgid.link/20260503201029.106481-1-pbonzini@redhat.com/
	Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 0cb2af2)
	Signed-off-by: Jonathan Maple <jmaple@ciq.com>
jira KERNEL-1285
cve CVE-2026-53359
Rebuild_History Non-Buildable kernel-6.12.0-211.32.1.el10_2
commit-author Paolo Bonzini <pbonzini@redhat.com>
commit 81ccda3

Commit 0cb2af2 ("KVM: x86: Fix shadow paging use-after-free due
to unexpected GFN") fixed a shadow paging mismatch between stored and
computed GFNs; the bug could be triggered by changing a PDE mapping from
outside the guest, and then deleting a memslot.  The rmap_remove()
call would miss entries created after the PDE change because the GFN
of the leaf SPTE does not match the GFN of the struct kvm_mmu_page.

A similar hole however remains if the modified PDE points to a non-leaf
page.  In this case the gfn can be made to match, but the role does not
match: the original large 2MB page creates a kvm_mmu_page with direct=1,
while the new 4KB needs a kvm_mmu_page with direct=0.  However,
kvm_mmu_get_child_sp() does not compare the role, and therefore reuses
the page.

The next step is installing a leaf (4KB) SPTE on the new path which
records an rmap entry under the gfn resolved by the walk.  But when
that child is zapped its parent kvm_mmu_page has direct=1 and
kvm_mmu_page_get_gfn() computes the gfn for the 4KB page as
sp->gfn + index instead of using sp->shadowed_translation[] (or sp->gfns[]
in older kernels).  It therefore fails to remove the recorded entry.

When the memslot is dropped the shadow page is freed but the rmap
entry survives, as in the scenario that was already fixed.  Code that
later walks that gfn (dirty logging, MMU notifier invalidation, and
so on) dereferences an sptep that lies in the freed page, causing the
use-after-free.

Fixes: 2032a93 ("KVM: MMU: Don't allocate gfns page for direct mmu pages")
	Reported-by: Hyunwoo Kim <imv4bel@gmail.com>
	Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 81ccda3)
	Signed-off-by: Jonathan Maple <jmaple@ciq.com>
Rebuild_History BUILDABLE
Rebuilding Kernel from rpm changelog with Fuzz Limit: 87.50%
Number of commits in upstream range v6.12~1..kernel-mainline: 138299
Number of commits in rpm: 30
Number of commits matched with upstream: 28 (93.33%)
Number of commits in upstream but not in rpm: 138271
Number of commits NOT found in upstream: 2 (6.67%)

Rebuilding Kernel on Branch rocky10_2_rebuild_kernel-6.12.0-211.32.1.el10_2 for kernel-6.12.0-211.32.1.el10_2
Clean Cherry Picks: 28 (100.00%)
Empty Cherry Picks: 0 (0.00%)
_______________________________

Full Details Located here:
ciq/ciq_backports/kernel-6.12.0-211.32.1.el10_2/rebuild.details.txt

Includes:
* git commit header above
* Empty Commits with upstream SHA
* RPM ChangeLog Entries that could not be matched

Individual Empty Commit failures contained in the same containing directory.
The git message for empty commits will have the path for the failed commit.
File names are the first 8 characters of the upstream SHA
@PlaidCat PlaidCat self-assigned this Jul 10, 2026
@PlaidCat PlaidCat requested review from a team July 10, 2026 21:41

@kerneltoast kerneltoast left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🥌

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants