From nobody Sat Jul 25 20:04:28 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 B8F133F1ABD; Tue, 14 Jul 2026 08:13:47 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784016827; cv=none; b=eLpPy3podmMBSs9GErRBtB94U45EAS3zdfixDLimH35KJSLKaEsui5oXmZHnO85AtHy71AnEl3zWuCCvoH3O9hBfd1Fy4ICEkw2ZE9Y8OTHZa2wVOj58EIf1TApX2Vccgc2+MOiWTEInMCD/+WzdoU/ejkqGxBhvelFDM8LFgcw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784016827; c=relaxed/simple; bh=RvjQbJdGnRfcWP17EENNzDbEMnQA1YA2pSV1be2vObo=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=gqZV3IWlDugKaWh0KBBVToWboBCYghn8TQ8it/yk7gP4lOnOGKynlglgLaSyDHZkFJFo6e4P0fEnvKTBLHsaRUdVGuvS/1pJJFtfCesUH7jbHFkD+FsXFKrN7Augu/zIKz+JC1dM3CpO6X5wVHPqt9hu44H4ppWdubxZ9/hFJpA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=j8wtl7qo; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="j8wtl7qo" Received: by smtp.kernel.org (Postfix) with ESMTPS id 75AAEC2BCC9; Tue, 14 Jul 2026 08:13:47 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1784016827; bh=RvjQbJdGnRfcWP17EENNzDbEMnQA1YA2pSV1be2vObo=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=j8wtl7qoiifyIdWF0zd04bsLaogsJNBkweHl3KZ4J1WdwSyRQzNT6bKc4AyNx7E0O w/uA1UMMog9aKtmm1U60UcLDJL7++8TagMgO/UyH2ujuq2vIBi/8KnxDvkxj4fhn3J YxvOnx+itt3FIhSlzouTNgxOdR/JhoiKUtVQDQbD52BanfXQ3OoxWat6PWHkWULYur 3qMc599FBhULNmJXjtfPYFNUsjQ2F/DPfJQhxauY9tBDGK1thMsPqpXKTLZOqjj1Ls JMpryWdVfxQz+mmT2C5usLqg5E4T9I7tm0hpJJX0fxNgtjPy/pSKuneC1HPTcP55ee mahGDT0FydG6A== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 533ADC44507; Tue, 14 Jul 2026 08:13:47 +0000 (UTC) From: Xiubo Li via B4 Relay Date: Tue, 14 Jul 2026 16:13:43 +0800 Subject: [PATCH 1/2] ceph: fix UAF in __kick_flushing_caps() on cf entry freed during unlock Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260714-ceph-uaf-fixes-v1-1-5c7603c181d5@clyso.com> References: <20260714-ceph-uaf-fixes-v1-0-5c7603c181d5@clyso.com> In-Reply-To: <20260714-ceph-uaf-fixes-v1-0-5c7603c181d5@clyso.com> To: Ilya Dryomov , Alex Markuze , Viacheslav Dubeyko Cc: ceph-devel@vger.kernel.org, linux-kernel@vger.kernel.org, Xiubo Li X-Mailer: b4 0.15.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1784016823; l=2921; i=xiubo.li@clyso.com; s=20260625; h=from:subject:message-id; bh=tlJhoiuAEKQm//eDIvba9hTyNJLYASywp02PhL5yfYc=; b=a3toyJf6mIBd3CkBovwuabNbbtDjqEROsqRM3iqQhrEUj/ZEDRKPQC+YF+9dBTrTsrl/qPLLL FUj/df/BwHKBUhI7wFEhj5kX9FWUCWi/aSs7VdgMUQUlGe2j9ymX2DR X-Developer-Key: i=xiubo.li@clyso.com; a=ed25519; pk=V3NGr0AgAopiUhaLY51ipBkLN5LlcLhjOEfLEq1RoZ8= X-Endpoint-Received: by B4 Relay for xiubo.li@clyso.com/20260625 with auth_id=840 X-Original-From: Xiubo Li Reply-To: xiubo.li@clyso.com From: Xiubo Li list_for_each_entry() iterates ci->i_cap_flush_list but drops i_ceph_lock to send cap messages. During the unlock window, handle_cap_flush_ack() can acquire i_ceph_lock, detach cf entries with tid <=3D flush_tid from the list, release i_ceph_lock, and free them via ceph_free_cap_flush() outside any lock. When the original thread reacquires i_ceph_lock and the for-loop macro advances via cf =3D list_next_entry(cf, i_list), it dereferences cf->i_list.next on freed memory. The race timeline: __kick_flushing_caps() handle_cap_flush_ack() ----------------------- ----------------------- holds i_ceph_lock <--- iterates to cf (tid=3D10) prepares FLUSH message drops i_ceph_lock <--- __send_cap() =E2=94=80=E2=94=80 FLUSH(tid=3D10) MDS sends FLUSH_ACK(tid=3D10) ---> acquires i_ceph_lock cf->tid(10) <=3D flush_tid(10), detaches cf from i_cap_flush_list drops i_ceph_lock ceph_free_cap_flush(cf) <- frees it! acquires i_ceph_lock <--- for-loop advances: cf =3D list_next_entry(cf, i_list) -- UAF on freed cf->i_list.next The cf was just sent by __kick_flushing_caps itself via __send_cap(). The MDS may respond with FLUSH_ACK quickly enough that handle_cap_flush_ack() frees cf before __kick_flushing_caps can finish the iteration. Fix by converting to a manual while loop: save the next pointer under i_ceph_lock before dropping it, then use the saved pointer after reacquiring, so the potentially-freed cf is never accessed again. Signed-off-by: Xiubo Li Reviewed-by: Viacheslav Dubeyko --- fs/ceph/caps.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index f8d898ad091e..358ee4f14524 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -2626,9 +2626,14 @@ static void __kick_flushing_caps(struct ceph_mds_cli= ent *mdsc, } } =20 - list_for_each_entry(cf, &ci->i_cap_flush_list, i_list) { - if (cf->tid < first_tid) + cf =3D list_first_entry(&ci->i_cap_flush_list, struct ceph_cap_flush, i_l= ist); + while (&cf->i_list !=3D &ci->i_cap_flush_list) { + struct ceph_cap_flush *next; + + if (cf->tid < first_tid) { + cf =3D list_next_entry(cf, i_list); continue; + } =20 cap =3D ci->i_auth_cap; if (!(cap && cap->session =3D=3D session)) { @@ -2638,6 +2643,7 @@ static void __kick_flushing_caps(struct ceph_mds_clie= nt *mdsc, } =20 first_tid =3D cf->tid + 1; + next =3D list_next_entry(cf, i_list); =20 if (!cf->is_capsnap) { struct cap_msg_args arg; @@ -2678,6 +2684,7 @@ static void __kick_flushing_caps(struct ceph_mds_clie= nt *mdsc, } =20 spin_lock(&ci->i_ceph_lock); + cf =3D next; } } =20 --=20 2.53.0 From nobody Sat Jul 25 20:04:28 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 B8D603EC2EF; Tue, 14 Jul 2026 08:13:47 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784016827; cv=none; b=teyFCBsN3KND/0uLnbBNEyTT+BWreM9+58hDAlIp+QTsXikSJW8fEW/hl/oLLKlpH20nfTdC/o5l6+lXZMkjc96K/rr7HCR7AkRVjIebXfthGtNFJbv6KKGLhk1orIJMy06GdlGqKTxQ7ibDRd65e6tMSysG6sCmTsyx5QgVgys= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784016827; c=relaxed/simple; bh=nBwhCRrTEgywUwVPUDHf5OG3kRhwOWjo4FzVFfZi38k=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=T0Y0kP6aT73MZGi95ICBwt+djy9tlL4wrl5ELwzrk6HvdJcqbKKvh3shWHxTg7HNUQcYngG7LznKrPkfXTLCyPZbJw4nfYGblcG4edDOYmxcTZH5d/fY/4K551OZa9bEIKiECR9mYd6sT2oRknUtb4pdHWeFZFSCB2aajzi7bQ4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=ujH8/hj5; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="ujH8/hj5" Received: by smtp.kernel.org (Postfix) with ESMTPS id 812BBC2BCF4; Tue, 14 Jul 2026 08:13:47 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1784016827; bh=nBwhCRrTEgywUwVPUDHf5OG3kRhwOWjo4FzVFfZi38k=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=ujH8/hj5z9tox4llsXtbbJIPzaeZ12U09DMIo3AiY0Ivx2B26vMwcTwUeOWTKAuhh fjaE9lWSOoE8PLKU+SdxUI9VGKkK4AuQVTf8Lj3n6rbfMM0NbvI/PvfFNQRKZDO3XE iwb4QbYXQ08HAb8ktclpXsWVTbPN4ZYhQYET2jpi0satjRzTOoUc5BR/6UiCGBqAp2 3SOrzk3Tp6UP2Xj/LSFdOONqOgfVscJcpqYyTSA0dfZZTSyacnr/rqXPqlrWGZJFnf Y2GfrkS4yyd0DkGXZGtNzbHVguN4Y5JD/qaN4ZY0+2zbR5nCxFgip7Y7PkwzT5xPjy zw2aNf2pU6szA== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 65D5CC44508; Tue, 14 Jul 2026 08:13:47 +0000 (UTC) From: Xiubo Li via B4 Relay Date: Tue, 14 Jul 2026 16:13:44 +0800 Subject: [PATCH 2/2] ceph: fix UAF in check_new_map() on session freed during unlock Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260714-ceph-uaf-fixes-v1-2-5c7603c181d5@clyso.com> References: <20260714-ceph-uaf-fixes-v1-0-5c7603c181d5@clyso.com> In-Reply-To: <20260714-ceph-uaf-fixes-v1-0-5c7603c181d5@clyso.com> To: Ilya Dryomov , Alex Markuze , Viacheslav Dubeyko Cc: ceph-devel@vger.kernel.org, linux-kernel@vger.kernel.org, Xiubo Li X-Mailer: b4 0.15.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1784016823; l=3431; i=xiubo.li@clyso.com; s=20260625; h=from:subject:message-id; bh=cLLPkWPffOP0JKb2qq7WN/4nmxNsUD+aO9DEMGGbqyU=; b=6iKVb7evR7ipKUrozYO44Q82JuquDMPDii+mOpmEHE5J4hLJJzY23/Hzvwe7u1+jkFgw3pok2 2xP1ROUEgyYCnq0Fnqoen69c5QV8NdGdWe9i3T67ADjSX8ZWmGDtdZ/ X-Developer-Key: i=xiubo.li@clyso.com; a=ed25519; pk=V3NGr0AgAopiUhaLY51ipBkLN5LlcLhjOEfLEq1RoZ8= X-Endpoint-Received: by B4 Relay for xiubo.li@clyso.com/20260625 with auth_id=840 X-Original-From: Xiubo Li Reply-To: xiubo.li@clyso.com From: Xiubo Li check_new_map() iterates mdsc->sessions[] and for each active session drops mdsc->mutex to perform per-session operations. The forced-close path (rank removed from map) correctly takes a reference on s via ceph_get_mds_session() before releasing mdsc->mutex, but three other paths do not: Path A (address changed): mutex_unlock =E2=86=92 mutex_lock(&s->s_mutex) Path B (reconnect): mutex_unlock =E2=86=92 send_mds_reconnect(mdsc= , s) Path C (active transition): mutex_unlock =E2=86=92 mutex_lock(&s->s_mutex) Without the extra reference, another thread can acquire mdsc->mutex during the unlock window, call __unregister_session() which drops the last reference on s, and free it. The original thread then accesses freed memory via s->s_mutex. Fix by adding ceph_get_mds_session(s) before each mutex_unlock and ceph_put_mds_session(s) after the corresponding mutex_lock, matching the pattern already used in the forced-close path. Race timeline (Path A): Thread A (check_new_map) Thread B (another map update holds mdsc->mutex or session teardown) -------------------------- -------------------------- s =3D mdsc->sessions[i] (refcount =3D=3D 1, held only by sessions[] array) mutex_unlock(&mdsc->mutex) ---> acquires mdsc->mutex __unregister_session(mdsc, s) sessions[i] =3D NULL ceph_put_mds_session(s) refcount: 1 -> 0 kfree(s) <--- freed! mutex_lock(&s->s_mutex) UAF on freed s->s_mutex Signed-off-by: Xiubo Li Reviewed-by: Viacheslav Dubeyko --- fs/ceph/mds_client.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 18be36c2e9e8..00a401f9f73b 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -5827,9 +5827,11 @@ static void check_new_map(struct ceph_mds_client *md= sc, ceph_mdsmap_get_addr(newmap, i), sizeof(struct ceph_entity_addr))) { /* just close it */ + ceph_get_mds_session(s); mutex_unlock(&mdsc->mutex); mutex_lock(&s->s_mutex); mutex_lock(&mdsc->mutex); + ceph_put_mds_session(s); ceph_con_close(&s->s_con); mutex_unlock(&s->s_mutex); s->s_state =3D CEPH_MDS_SESSION_RESTARTING; @@ -5844,6 +5846,7 @@ static void check_new_map(struct ceph_mds_client *mds= c, newstate >=3D CEPH_MDS_STATE_RECONNECT) { int rc; =20 + ceph_get_mds_session(s); mutex_unlock(&mdsc->mutex); clear_bit(i, targets); rc =3D send_mds_reconnect(mdsc, s); @@ -5852,6 +5855,7 @@ static void check_new_map(struct ceph_mds_client *mds= c, "mds%d reconnect failed: %d\n", i, rc); mutex_lock(&mdsc->mutex); + ceph_put_mds_session(s); } =20 /* @@ -5864,9 +5868,11 @@ static void check_new_map(struct ceph_mds_client *md= sc, pr_info_client(cl, "mds%d recovery completed\n", s->s_mds); kick_requests(mdsc, i); + ceph_get_mds_session(s); mutex_unlock(&mdsc->mutex); mutex_lock(&s->s_mutex); mutex_lock(&mdsc->mutex); + ceph_put_mds_session(s); ceph_kick_flushing_caps(mdsc, s); mutex_unlock(&s->s_mutex); wake_up_session_caps(s, RECONNECT); --=20 2.53.0