[PATCH 9/9] rbd: fix osdmap use-after-free in pool lookups
From: Max Kellermann
Date: Wed Nov 11 2026 - 11:21:55 EST
rbd_spec_fill_names() and do_rbd_add() look up pools in osdc->osdmap
without holding osdc->lock, but ceph_osdc_handle_map() replaces the
osdmap and frees the old one under that lock (held for writing). An
"rbd map" racing with an osdmap update can therefore walk the freed
pool tree, and rbd_spec_fill_names() can kstrdup() a pool name that
has been freed already.
Do the lookups while holding osdc->lock for reading, and copy the pool
name before releasing it. Use GFP_NOIO for the copy, because reclaim
could issue I/O to an rbd device, which needs osdc->lock while a writer
may be waiting for it.
Fixes: 9e15b77d9af3 ("rbd: get additional info in parent spec")
Fixes: 30ba1f020221 ("rbd: make sure we have latest osdmap on 'rbd map'")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Max Kellermann <max.kellermann@xxxxxxxxx>
---
drivers/block/rbd.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c
index 96743c059d98..d287c4882ca0 100644
--- a/drivers/block/rbd.c
+++ b/drivers/block/rbd.c
@@ -6001,14 +6001,21 @@ static int rbd_spec_fill_names(struct rbd_device *rbd_dev)
rbd_assert(spec->image_id);
rbd_assert(spec->snap_id != CEPH_NOSNAP);
- /* Get the pool name; we have to make our own copy of this */
+ /*
+ * Get the pool name; we have to make our own copy of this
+ * because ceph_osdc_handle_map() replaces and frees the
+ * osdmap
+ */
+ down_read(&osdc->lock);
pool_name = ceph_pg_pool_name_by_id(osdc->osdmap, spec->pool_id);
if (!pool_name) {
+ up_read(&osdc->lock);
rbd_warn(rbd_dev, "no pool with id %llu", spec->pool_id);
return -EIO;
}
- pool_name = kstrdup(pool_name, GFP_KERNEL);
+ pool_name = kstrdup(pool_name, GFP_NOIO);
+ up_read(&osdc->lock);
if (!pool_name)
return -ENOMEM;
@@ -7100,7 +7107,9 @@ static ssize_t do_rbd_add(const char *buf, size_t count)
}
/* pick the pool */
+ down_read(&rbdc->client->osdc.lock);
rc = ceph_pg_poolid_by_name(rbdc->client->osdc.osdmap, spec->pool_name);
+ up_read(&rbdc->client->osdc.lock);
if (rc < 0) {
if (rc == -ENOENT)
pr_info("pool %s does not exist\n", spec->pool_name);
--
2.53.0