From nobody Mon Sep 28 07:18:18 2026 Received: from out162-62-57-49.mail.qq.com (out162-62-57-49.mail.qq.com [162.62.57.49]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id ABEA53EB0FA for ; Tue, 25 Aug 2026 09:13:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=162.62.57.49 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787649220; cv=none; b=l2D7A8HvGJue4OOsNMnp+ab59yU0hSwOHfe4Skq/AAgaRedV2+FBU9ImXRlMsVYIXByGLQAXyQwNg0gkxNURGjg+Sdmu0b9H9/jbT16tJ08XKUwXhkerRYZfFHXfdJz85W/43OPyAJ80RbnsWx2+L8UXoGojVun1po6Qxdkg4t0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787649220; c=relaxed/simple; bh=aLMwOWgS1UYjq1vewfAliYHENB1SJZ4WjwkN0VMmghE=; h=Message-ID:Date:MIME-Version:To:Cc:From:Subject:Content-Type; b=MjjykLKrVbHNDipJRwMYitP9RgEifmxt32KlURPNeyPJc+nDcVM8obdV8HrksfvTp7DCF1Qki+SVH/hrRr0qy3ksb86xi82qdXBVY/TIbw2UCud74DlnQRRJd5yPjARfPHdR1Qv2b1HlMjkNreCete1NNfoZHMlQeinpzSfwKJs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=qq.com; spf=pass smtp.mailfrom=qq.com; dkim=pass (1024-bit key) header.d=qq.com header.i=@qq.com header.b=e0S/jggJ; arc=none smtp.client-ip=162.62.57.49 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=qq.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=qq.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=qq.com header.i=@qq.com header.b="e0S/jggJ" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=qq.com; s=s201512; t=1787649213; bh=aLMwOWgS1UYjq1vewfAliYHENB1SJZ4WjwkN0VMmghE=; h=Date:To:Cc:From:Subject; b=e0S/jggJ25GUsHfMTprsoRPZoJbtUrq2lHxEmHVNG8s0Alvp3IVoRJZPvOVGFGXIN YkfU+9LyVz/4U7qi154EG9hVblBSHULvzR3VQMhQIdlyjZ02usU4sOns2/+RJfWNce S77lPi+mhp27rmFEy/xH8NY6WAd9p+GcGRAPmqHU= Received: from [192.168.255.10] ([111.206.145.19]) by newxmesmtplogicsvrszb51-1.qq.com (NewEsmtp) with SMTP id 35F27A39; Tue, 25 Aug 2026 17:13:31 +0800 X-QQ-mid: xmsmtpt1787649211tz6rwoi9y Message-ID: X-QQ-XMAILINFO: OATpkVjS499uFZrDcsRokjD9LucosXi5j19jMq/Jj3aDxdbo5+egkfO0GN25x1 fjvZTiOWENx0lGSv6SdUHR9G3I6FRnkveIQSgiKPNL3pi80W5r8cFJvvtxpgCSP9TYmkdyJe0Yym HHwZJUT1nnevl6oCaPg2C+Bw/WpZxb8iPKdFvo2iuGxMd00Z/X8IMQCwGaIFFxvrX0/9ZlMKIN8H GibS2599KpSQavlYEOldvE0KQEUmGU2xcQe7xiToIH3Kyo/mJtJQvP+WBO+rGRD7ze9Rn/wytbDc ZzojQrXP4Dl8gv2zNOZK1j4C/1GnSdXYH8HSXDDwgkJUPm2MgOT9PQ4jxbZrbVhJTSy4tFbhckpc k9np99nZfAPZvftSbuRXwimi1NtuCsZyELy1xH3TsghDK0u2qr0Lq68tT36yA1zExC0fg0UEKupv 4Jz+moOfKdqL2wD2QpsZKHiwE+d5wV0YxC0icqnzATOVpwjE5nh59/Hzy5zlsqrseHzx+BcoiFYg ECGnU2VLVowv6d8Mxql94xuYPnvdSXrbTQssmOPyKmiAlf194et7B1DG/F+q7sM0ls7SWeeFxFOy 06AK9YRnBCT7nIMGbOO1IOs8Lu8AecDvJZndD7fhcbLG9Tjs9DWNpCs8/JOF5+PnVWT1PO4bvUhI ppklNPMwCLBvMrEjEprc2LpOV3awVJHAjuKIZLewWHXKdTSSZpxwXx9Piv/n0Q7GDtvxpq9LCzGl SQWq7Mf7A69bLY6g1CDpfOals1yWmCpb0HGGP1jdIHvENH+VheGcUXW77fuK9LltMjebIxUpCH32 5+e3YwSQvIvajVQVMP4tgRjJ5hPndX23iCFqTu3/Wh8XBteK6QZxpnbxrqfhq/C8Tx17Vb+WPGuI YVhJ7rnUauonb3Z56p6RklhFVzUZ23+m3vlO+w/gWsuD2IqImPF12QLorcnGo6mu0bjyuCtwB8N3 7nWMiyqaB7EtQCeYVRXG79aJ5C6iawrN1ksqQpkHqSftrKAc9CndwCmmg5sQp26aJq7ufHs1raT5 GWqj9ba41q0acwHCFpRunT7A6yf0J926zfvF6MiCrLAooyVIYpHVE62UoKuR2hIEXlqH2YAiP/OR M4TSNqp+Zozryoytq2oTEM/j/Xbgmb1xO8SW/tMwdYk8EzFV50vRA5yQL9WcsCePGwEpbq X-QQ-XMRINFO: Nq+8W0+stu50tPAe92KXseR0ZZmBTk3gLg== X-OQ-MSGID: Date: Tue, 25 Aug 2026 17:13:30 +0800 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 User-Agent: Mozilla Thunderbird To: boqun@kernel.org, longman@redhat.com, peterz@infradead.org, mingo@redhat.com, will@kernel.org Cc: linux-kernel@vger.kernel.org From: Yang Zi <2959243019@qq.com> Subject: [PATCH] locking/mutex: Restore RCU read-side protection for optimistic spinning Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable The optimistic spinning paths in kernel/locking/mutex.c dereference the current lock owner's task_struct (via owner_on_cpu() -> owner->on_cpu and task_cpu(owner)) without any lifetime protection on the owner pointer. __mutex_owner() (kernel/locking/mutex.h) returns a bare task_struct pointer that is read directly from lock->owner. mutex_spin_on_owner() and mutex_can_spin_on_owner() then dereference it inside "while (__mutex_owner(lock) =3D=3D owner)" / immediately after the owner load, whi= le relying only on preempt_disable(). Since commit 6c2787f2a20c ("locking: Remove rcu_read_{,un}lock() for preempt_{dis,en}able()"), the code has assumed that preempt_disable() is equivalent to an RCU read-side critical section and therefore that the owner's task_struct "won't go away during the spinning period". That assumption does not hold on PREEMPT_LAZY kernels: preempt_disable() there no longer actually disables preemption, so the RCU callback that frees a just-exited owner's task_struct (put_task_struct_rcu_user() -> call_rcu(), run in rcu_do_batch()) can fire and free the object while a spinner still holds the stale pointer, leading to a slab use-after-free read of owner->on_cpu. This manifests in three independent syzkaller/KASAN reports with the same root cause: - BUG 123 (dw_edma_pcie, v7.1, PREEMPT(lazy)): finit_module -> =C2=A0 driver_register -> bus_add_driver -> bus_for_each_dev -> __driver_at= tach =C2=A0 -> device_lock -> __mutex_lock -> mutex_optimistic_spin -> =C2=A0 mutex_can_spin_on_owner -> owner_on_cpu. - BUG 132 (bna): read(2) -> uevent_show -> device_lock -> =C2=A0 mutex_optimistic_spin -> mutex_can_spin_on_owner -> owner_on_cpu. - BUG 208 (iavf, v7.1, PREEMPT(lazy)): delete_module -> =C2=A0 pci_unregister_driver -> ... -> iavf_remove -> netdev_lock -> =C2=A0 __mutex_lock -> mutex_optimistic_spin -> mutex_can_spin_on_owner -> =C2=A0 owner_on_cpu. Fix it by taking an explicit rcu_read_lock()/rcu_read_unlock() around the owner dereference in both mutex_spin_on_owner() and mutex_can_spin_on_owner= (), restoring the pre-5.16 protection, and update the now-inaccurate comments. Signed-off-by: Yang Zi <2959243019@qq.com> --- diff --git a/kernel/locking/mutex.c b/kernel/locking/mutex.c index 942a939cee95..cc2d7090bf36 100644 --- a/kernel/locking/mutex.c +++ b/kernel/locking/mutex.c @@ -389,14 +389,21 @@ bool mutex_spin_on_owner(struct mutex *lock, struct t= ask_struct *owner, =C2=A0 =C2=A0 =C2=A0 =C2=A0lockdep_assert_preemption_disabled(); =C2=A0 +=C2=A0 =C2=A0 /* +=C2=A0 =C2=A0 =C2=A0* Optimistic spinners run with preempt_disable() which= , on classic +=C2=A0 =C2=A0 =C2=A0* PREEMPT_RCU, was enough to act as an RCU read-side c= ritical +=C2=A0 =C2=A0 =C2=A0* section. On PREEMPT_LAZY kernels preempt_disable() n= o longer +=C2=A0 =C2=A0 =C2=A0* prevents RCU callbacks (e.g. the free of an exiting = owner's +=C2=A0 =C2=A0 =C2=A0* task_struct via call_rcu() in rcu_do_batch()) from r= unning, so take +=C2=A0 =C2=A0 =C2=A0* an explicit RCU read-side critical section to keep t= he owner alive. +=C2=A0 =C2=A0 =C2=A0*/ +=C2=A0 =C2=A0 rcu_read_lock(); =C2=A0 =C2=A0 =C2=A0while (__mutex_owner(lock) =3D=3D owner) { =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0/* -=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* Ensure we emit the owner->on_cpu, dere= ference _after_ -=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* checking lock->owner still matches own= er. And we already -=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* disabled preemption which is equal to = the RCU read-side -=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* crital section in optimistic spinning = code. Thus the -=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* task_strcut structure won't go away du= ring the spinning -=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* period +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* Ensure we emit the owner->on_cpu deref= erence _after_ +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* checking lock->owner still matches own= er. If that fails, +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* owner might point to freed memory. If = it still matches, +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* the rcu_read_lock() ensures the memory= stays valid. =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 */ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0barrier(); =C2=A0 @@ -415,6 +422,7 @@ bool mutex_spin_on_owner(struct mutex *lock, struct tas= k_struct *owner, =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0cpu_relax(); =C2=A0 =C2=A0 =C2=A0} +=C2=A0 =C2=A0 rcu_read_unlock(); =C2=A0 =C2=A0 =C2=A0 =C2=A0return ret; =C2=A0} @@ -433,13 +441,16 @@ static inline int mutex_can_spin_on_owner(struct mute= x *lock) =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return 0; =C2=A0 =C2=A0 =C2=A0 =C2=A0/* -=C2=A0 =C2=A0 =C2=A0* We already disabled preemption which is equal to the= RCU read-side -=C2=A0 =C2=A0 =C2=A0* crital section in optimistic spinning code. Thus the= task_strcut -=C2=A0 =C2=A0 =C2=A0* structure won't go away during the spinning period. +=C2=A0 =C2=A0 =C2=A0* See the comment in mutex_spin_on_owner(): preempt_di= sable() is no +=C2=A0 =C2=A0 =C2=A0* longer an RCU read-side critical section on PREEMPT_= LAZY kernels, +=C2=A0 =C2=A0 =C2=A0* so protect the owner dereference with an explicit RC= U read-side +=C2=A0 =C2=A0 =C2=A0* critical section. =C2=A0 =C2=A0 =C2=A0 */ +=C2=A0 =C2=A0 rcu_read_lock(); =C2=A0 =C2=A0 =C2=A0owner =3D __mutex_owner(lock); =C2=A0 =C2=A0 =C2=A0if (owner) =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0retval =3D owner_on_cpu(owner); +=C2=A0 =C2=A0 rcu_read_unlock(); =C2=A0 =C2=A0 =C2=A0 =C2=A0/* =C2=A0 =C2=A0 =C2=A0 * If lock->owner is not set, the mutex has been releas= ed. Return true