Fix rtmutex/futex self-deadlock NULL deref in remove_waiter() ahead of RHEL; bump to 687.22.2

This commit is contained in:
Andrew Lukoshko 2026-07-08 13:47:16 +00:00
parent d590ae2528
commit 819eaa69a5
13 changed files with 179 additions and 2 deletions

View File

@ -0,0 +1,90 @@
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
[ AlmaLinux: adapted to a9 (5.14.0-687.5.1.el9_8). No functional change vs
upstream commit 3bfdc63936dd; only diff hunk offsets differ. The
scoped_guard(raw_spinlock, ...) form is used verbatim: it is available in
this tree via include/linux/cleanup.h and DEFINE_LOCK_GUARD_1(raw_spinlock,
...) in include/linux/spinlock.h. ]
---
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 1eb4f9499..e881d9548 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.43.7

View File

@ -0,0 +1,77 @@
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
[ AlmaLinux: adapted to a9 (5.14.0-687.5.1.el9_8). No change vs upstream
commit 40a25d59e85b. Both hunks apply cleanly: kernel/locking/rtmutex.c
remove_waiter() (the waiter_task local comes from the earlier
"rtmutex: Use waiter::task ..." patch) and kernel/locking/rtmutex_api.c
rt_mutex_start_proxy_lock(). The "ret < 0" tightening is meaningful in this
tree: __rt_mutex_start_proxy_lock() returns 1 on try_to_take_rt_mutex()
success, so the prior "if (unlikely(ret))" wrongly invoked remove_waiter()
after acquiring the lock. ]
---
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 e881d9548..1cecc1092 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 6614ccdc1..341a540f6 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.43.7

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -176,13 +176,13 @@ Summary: The Linux kernel
# define buildid .local
%define specversion 5.14.0
%define patchversion 5.14
%define pkgrelease 687.22.1
%define pkgrelease 687.22.2
%define kversion 5
%define tarfile_release 5.14.0-687.5.1.el9_8
# This is needed to do merge window version magic
%define patchlevel 14
# This allows pkg_release to have configurable %%{?dist} tag
%define specrelease 687.22.1%{?buildid}%{?dist}
%define specrelease 687.22.2%{?buildid}%{?dist}
# This defines the kabi tarball version
%define kabiversion 5.14.0-687.5.1.el9_8
@ -1612,6 +1612,8 @@ Patch1732: 1732-net-gro-don-t-merge-zcopy-skbs.patch
Patch1733: 1733-xfrm-defensively-unhash-xfrm-state-lists-in-xfrm-state-delet.patch
Patch1734: 1734-smb-client-fix-off-by-8-bounds-check-in-check-wsl-eas.patch
Patch1735: 1735-smb-client-fix-out-of-bounds-read-in-smb2-compound-op.patch
Patch1736: 1736-rtmutex-use-waiter-task-in-remove-waiter.patch
Patch1738: 1738-rtmutex-skip-remove-waiter-when-not-enqueued.patch
# END OF PATCH DEFINITIONS
%description
@ -2991,6 +2993,8 @@ ApplyPatch 1732-net-gro-don-t-merge-zcopy-skbs.patch
ApplyPatch 1733-xfrm-defensively-unhash-xfrm-state-lists-in-xfrm-state-delet.patch
ApplyPatch 1734-smb-client-fix-off-by-8-bounds-check-in-check-wsl-eas.patch
ApplyPatch 1735-smb-client-fix-out-of-bounds-read-in-smb2-compound-op.patch
ApplyPatch 1736-rtmutex-use-waiter-task-in-remove-waiter.patch
ApplyPatch 1738-rtmutex-skip-remove-waiter-when-not-enqueued.patch
# END OF PATCH APPLICATIONS
# Any further pre-build tree manipulations happen here.
@ -5065,6 +5069,12 @@ fi
#
#
%changelog
* Tue Jul 08 2026 Andrew Lukoshko <alukoshko@almalinux.org> - 5.14.0-687.22.2
- Fix rtmutex self-deadlock NULL pointer dereference in remove_waiter() ahead of
RHEL, upstream 3bfdc63936dd + 40a25d59e85b (1736, 1738); the futex_requeue guard
74e144274af3 was dropped, reverted upstream by 39def6d250d3 (the NULL deref is
fully handled by 40a25d59e85b)
* Mon Jul 07 2026 Andrew Lukoshko <alukoshko@almalinux.org> - 5.14.0-687.22.1
- Recreate RHEL 5.14.0-687.22.1 from CentOS Stream 9 and upstream stable backports (1713-1735)
- Retain AlmaLinux ahead-of-RHEL fixes: KVM x86 shadow paging (1710-1711), eventpoll