[PATCH 1/1] libceph: unpublish osdmap before destroying it on stop

Ren Wei <[email protected]> Mon, 20 Jul 2026 11:49:06 +0800
Newsgroups org.kernel.vger.ceph-devel
Message-ID <bbcdd44d07efd11c7211195b6302b179918cb253.1784371311.git.xizh2024@lzu.edu.cn>
From: Zihan Xi <[email protected]>

osdmap_show() takes osdc->lock for reading and then walks
osdc->osdmap while formatting the debugfs osdmap file. During client
teardown, ceph_osdc_stop() closes the OSD state under the write side of
that lock, drops the lock, and only then destroys osdc->osdmap while the
pointer is still published.

A concurrent debugfs reader can therefore acquire the read side after the
stop path has dropped it and traverse an osdmap that is being freed or has
already been freed.

Take the osdmap pointer out of osdc under the write lock before dropping
that lock, then destroy the saved pointer after unlocking. This makes the
write lock wait for any current osdmap_show() reader and makes later
readers observe a NULL osdmap instead of a freed object. Also make queued
OSD map messages return after taking the lock if teardown has already
unpublished the osdmap.

Fixes: 76aa844d5b2f ("ceph: debugfs")
Cc: [email protected]
Reported-by: Vega <[email protected]>
Assisted-by: Codex:gpt-5.4
Signed-off-by: Zihan Xi <[email protected]>
Reviewed-by: Ren Wei <[email protected]>
---
 net/ceph/osd_client.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c
index 2ff00070c181..70467eede336 100644
--- a/net/ceph/osd_client.c
+++ b/net/ceph/osd_client.c
@@ -4145,8 +4145,11 @@ void ceph_osdc_handle_map(struct ceph_osd_client *osdc, struct ceph_msg *msg)
 	bool pauserd, pausewr;
 	int err;
 
-	dout("%s have %u\n", __func__, osdc->osdmap->epoch);
 	down_write(&osdc->lock);
+	if (!osdc->osdmap)
+		goto out_unlock;
+
+	dout("%s have %u\n", __func__, osdc->osdmap->epoch);
 
 	/* verify fsid */
 	ceph_decode_need(&p, end, sizeof(fsid), bad);
@@ -4238,6 +4241,7 @@ void ceph_osdc_handle_map(struct ceph_osd_client *osdc, struct ceph_msg *msg)
 bad:
 	pr_err("osdc handle_map corrupt msg\n");
 	ceph_msg_dump(msg);
+out_unlock:
 	up_write(&osdc->lock);
 }
 
@@ -5268,6 +5272,8 @@ int ceph_osdc_init(struct ceph_osd_client *osdc, struct ceph_client *client)
 
 void ceph_osdc_stop(struct ceph_osd_client *osdc)
 {
+	struct ceph_osdmap *osdmap;
+
 	destroy_workqueue(osdc->completion_wq);
 	destroy_workqueue(osdc->notify_wq);
 	cancel_delayed_work_sync(&osdc->timeout_work);
@@ -5279,6 +5285,8 @@ void ceph_osdc_stop(struct ceph_osd_client *osdc)
 						struct ceph_osd, o_node);
 		close_osd(osd);
 	}
+	osdmap = osdc->osdmap;
+	osdc->osdmap = NULL;
 	up_write(&osdc->lock);
 	WARN_ON(refcount_read(&osdc->homeless_osd.o_ref) != 1);
 	osd_cleanup(&osdc->homeless_osd);
@@ -5290,7 +5298,7 @@ void ceph_osdc_stop(struct ceph_osd_client *osdc)
 	WARN_ON(atomic_read(&osdc->num_requests));
 	WARN_ON(atomic_read(&osdc->num_homeless));
 
-	ceph_osdmap_destroy(osdc->osdmap);
+	ceph_osdmap_destroy(osdmap);
 	mempool_destroy(osdc->req_mempool);
 	ceph_msgpool_destroy(&osdc->msgpool_op);
 	ceph_msgpool_destroy(&osdc->msgpool_op_reply);
-- 
2.43.0