Skip to content

[rocky9_8] History Rebuild through kernel-5.14.0-687.24.1.el9_8#1425

Open
PlaidCat wants to merge 21 commits into
rocky9_8from
rocky9_8_rebuild
Open

[rocky9_8] History Rebuild through kernel-5.14.0-687.24.1.el9_8#1425
PlaidCat wants to merge 21 commits into
rocky9_8from
rocky9_8_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 (5.14.0-687)
    • 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-5.14.0-687.24.1.el9_8

$ cat ciq/ciq_backports/kernel-5.14.0-687.24.1.el9_8/rebuild.details.txt
Rebuild_History BUILDABLE
Rebuilding Kernel from rpm changelog with Fuzz Limit: 87.50%
Number of commits in upstream range v5.14~1..kernel-mainline: 394115
Number of commits in rpm: 23
Number of commits matched with upstream: 20 (86.96%)
Number of commits in upstream but not in rpm: 394095
Number of commits NOT found in upstream: 3 (13.04%)

Rebuilding Kernel on Branch rocky9_8_rebuild_kernel-5.14.0-687.24.1.el9_8 for kernel-5.14.0-687.24.1.el9_8
Clean Cherry Picks: 17 (85.00%)
Empty Cherry Picks: 3 (15.00%)
_______________________________

__EMPTY COMMITS__________________________
86e87059e6d1fd5115a31949726450ed03c1073b eventpoll: move epi_fget() up
d30deeb8b0cf6259785c1fb79b87905d281b0a5a eventpoll: move f_lock acquisition into ep_remove_file()
a573cb40f9819c3fe81a43eb10170f8fc8eddc5e eventpoll: refresh epi_fget() / ep_remove_file() comments

__CHANGES NOT IN UPSTREAM________________
Replace sbat with Rocky Linux sbat
Change bug tracker URL
Ensure appended release in sbat is removed'

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="-rocky9_8_rebuild-699395e66e0e"
Making olddefconfig
--
  HOSTCC  scripts/kconfig/util.o
  HOSTLD  scripts/kconfig/conf
#
# configuration written to .config
#
Starting Build
  SYSHDR  arch/x86/include/generated/uapi/asm/unistd_32.h
  SYSHDR  arch/x86/include/generated/uapi/asm/unistd_64.h
  SYSHDR  arch/x86/include/generated/uapi/asm/unistd_x32.h
  SYSTBL  arch/x86/include/generated/asm/syscalls_32.h
  SYSHDR  arch/x86/include/generated/asm/unistd_32_ia32.h
--
  BTF [M] sound/usb/usx2y/snd-usb-usx2y.ko
  BTF [M] sound/usb/usx2y/snd-usb-us144mkii.ko
  BTF [M] sound/virtio/virtio_snd.ko
  BTF [M] sound/x86/snd-hdmi-lpe-audio.ko
  BTF [M] sound/xen/snd_xen_front.ko
[TIMER]{BUILD}: 1581s
Making Modules
  INSTALL /lib/modules/5.14.0-rocky9_8_rebuild-699395e66e0e/kernel/arch/x86/crypto/blake2s-x86_64.ko
  INSTALL /lib/modules/5.14.0-rocky9_8_rebuild-699395e66e0e/kernel/arch/x86/crypto/blowfish-x86_64.ko
  INSTALL /lib/modules/5.14.0-rocky9_8_rebuild-699395e66e0e/kernel/arch/x86/crypto/camellia-aesni-avx-x86_64.ko
  INSTALL /lib/modules/5.14.0-rocky9_8_rebuild-699395e66e0e/kernel/arch/x86/crypto/camellia-aesni-avx2.ko
--
  STRIP   /lib/modules/5.14.0-rocky9_8_rebuild-699395e66e0e/kernel/sound/xen/snd_xen_front.ko
  SIGN    /lib/modules/5.14.0-rocky9_8_rebuild-699395e66e0e/kernel/sound/xen/snd_xen_front.ko
  SIGN    /lib/modules/5.14.0-rocky9_8_rebuild-699395e66e0e/kernel/sound/x86/snd-hdmi-lpe-audio.ko
  SIGN    /lib/modules/5.14.0-rocky9_8_rebuild-699395e66e0e/kernel/sound/usb/snd-usb-audio.ko
  DEPMOD  /lib/modules/5.14.0-rocky9_8_rebuild-699395e66e0e
[TIMER]{MODULES}: 11s
Making Install
sh ./arch/x86/boot/install.sh 5.14.0-rocky9_8_rebuild-699395e66e0e \
	arch/x86/boot/bzImage System.map "/boot"
[TIMER]{INSTALL}: 22s
Checking kABI
kABI check passed
Setting Default Kernel to /boot/vmlinuz-5.14.0-rocky9_8_rebuild-699395e66e0e and Index to 0
Hopefully Grub2.0 took everything ... rebooting after time metrices
[TIMER]{MRPROPER}: 6s
[TIMER]{BUILD}: 1581s
[TIMER]{MODULES}: 11s
[TIMER]{INSTALL}: 22s
[TIMER]{TOTAL} 1626s
Rebooting in 10 seconds

KSelfTests

$ get_kselftest_diff.sh
kselftest.5.14.0-rocky9_8_rebuild-37e80bde7a74.log
311
kselftest.5.14.0-rocky9_8_rebuild-ccaa0c849bbe.log
311
kselftest.5.14.0-rocky9_8_rebuild-68774cfe924a.log
311
kselftest.5.14.0-rocky9_8_rebuild-699395e66e0e.log
311
Before: kselftest.5.14.0-rocky9_8_rebuild-68774cfe924a.log
After: kselftest.5.14.0-rocky9_8_rebuild-699395e66e0e.log
Diff:
No differences found.

PlaidCat added 21 commits July 10, 2026 00:01
jira KERNEL-1279
Rebuild_History Non-Buildable kernel-5.14.0-687.24.1.el9_8
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-1279
Rebuild_History Non-Buildable kernel-5.14.0-687.24.1.el9_8
commit-author Yiming Qian <yimingqian591@gmail.com>
commit 67ba971

The ebtables SNAT target keeps the Ethernet source address rewrite
behind skb_ensure_writable(skb, 0).  This is intentional: at the bridge
ebtables hooks the Ethernet header is addressed through
skb_mac_header()/eth_hdr(), while skb->data points at the Ethernet
payload.  Asking skb_ensure_writable() for ETH_HLEN bytes would check
the payload, not the Ethernet header, and would reintroduce the small
packet regression fixed by commit 63137bc.

However, the optional ARP sender hardware address rewrite is different.
It writes through skb_store_bits() at an offset relative to skb->data:

        skb_store_bits(skb, sizeof(struct arphdr), info->mac, ETH_ALEN)

skb_header_pointer() only safely reads the ARP header; it does not make
the later sender hardware address range writable.  If that range is
still held in a nonlinear skb fragment backed by a splice-imported file
page, skb_store_bits() maps the frag page and copies the new MAC address
directly into it.

Ensure the ARP SHA range is writable before reading the ARP header and
before calling skb_store_bits().

Fixes: 63137bc ("netfilter: ebtables: Fixes dropping of small packets in bridge nat")
	Reported-by: Yiming Qian <yimingqian591@gmail.com>
	Signed-off-by: Yiming Qian <yimingqian591@gmail.com>
	Signed-off-by: Florian Westphal <fw@strlen.de>
	Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
(cherry picked from commit 67ba971)
	Signed-off-by: Jonathan Maple <jmaple@ciq.com>
jira KERNEL-1279
Rebuild_History Non-Buildable kernel-5.14.0-687.24.1.el9_8
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-1279
cve CVE-2026-43074
Rebuild_History Non-Buildable kernel-5.14.0-687.24.1.el9_8
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-1279
Rebuild_History Non-Buildable kernel-5.14.0-687.24.1.el9_8
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-1279
Rebuild_History Non-Buildable kernel-5.14.0-687.24.1.el9_8
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-1279
Rebuild_History Non-Buildable kernel-5.14.0-687.24.1.el9_8
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-1279
Rebuild_History Non-Buildable kernel-5.14.0-687.24.1.el9_8
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-1279
Rebuild_History Non-Buildable kernel-5.14.0-687.24.1.el9_8
commit-author Christian Brauner <brauner@kernel.org>
commit 86e8705
Empty-Commit: Cherry-Pick Conflicts during history rebuild.
Will be included in final tarball splat. Ref for failed cherry-pick at:
ciq/ciq_backports/kernel-5.14.0-687.24.1.el9_8/86e87059.failed

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>

# Conflicts:
#	fs/eventpoll.c
jira KERNEL-1279
Rebuild_History Non-Buildable kernel-5.14.0-687.24.1.el9_8
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-1279
cve CVE-2026-46242
Rebuild_History Non-Buildable kernel-5.14.0-687.24.1.el9_8
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-1279
Rebuild_History Non-Buildable kernel-5.14.0-687.24.1.el9_8
commit-author Christian Brauner <brauner@kernel.org>
commit d30deeb
Empty-Commit: Cherry-Pick Conflicts during history rebuild.
Will be included in final tarball splat. Ref for failed cherry-pick at:
ciq/ciq_backports/kernel-5.14.0-687.24.1.el9_8/d30deeb8.failed

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>

# Conflicts:
#	fs/eventpoll.c
jira KERNEL-1279
Rebuild_History Non-Buildable kernel-5.14.0-687.24.1.el9_8
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-1279
Rebuild_History Non-Buildable kernel-5.14.0-687.24.1.el9_8
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-1279
Rebuild_History Non-Buildable kernel-5.14.0-687.24.1.el9_8
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-1279
Rebuild_History Non-Buildable kernel-5.14.0-687.24.1.el9_8
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-1279
Rebuild_History Non-Buildable kernel-5.14.0-687.24.1.el9_8
commit-author Christian Brauner <brauner@kernel.org>
commit a573cb4
Empty-Commit: Cherry-Pick Conflicts during history rebuild.
Will be included in final tarball splat. Ref for failed cherry-pick at:
ciq/ciq_backports/kernel-5.14.0-687.24.1.el9_8/a573cb40.failed

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>

# Conflicts:
#	fs/eventpoll.c
…in ets_qdisc_change

jira KERNEL-1279
cve CVE-2025-71066
Rebuild_History Non-Buildable kernel-5.14.0-687.24.1.el9_8
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-1279
cve CVE-2026-46113
Rebuild_History Non-Buildable kernel-5.14.0-687.24.1.el9_8
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-1279
cve CVE-2026-53359
Rebuild_History Non-Buildable kernel-5.14.0-687.24.1.el9_8
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 v5.14~1..kernel-mainline: 394115
Number of commits in rpm: 23
Number of commits matched with upstream: 20 (86.96%)
Number of commits in upstream but not in rpm: 394095
Number of commits NOT found in upstream: 3 (13.04%)

Rebuilding Kernel on Branch rocky9_8_rebuild_kernel-5.14.0-687.24.1.el9_8 for kernel-5.14.0-687.24.1.el9_8
Clean Cherry Picks: 17 (85.00%)
Empty Cherry Picks: 3 (15.00%)
_______________________________

Full Details Located here:
ciq/ciq_backports/kernel-5.14.0-687.24.1.el9_8/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 04:49

@bmastbergen bmastbergen 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.

3 participants