From: Juergen Gross Date: Thu, 5 Nov 2026 08:27:38 +0100 Subject: [PATCH] xen-blkback: Avoid UAF when reading I/O statistics MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reading I/O statistics via sysfs concurrently with tearing down a frontend device might result in UAF or a NULL dereference, as the reading of the statistics data is not synchronized with freeing the data. Use a rwlock for synchronizing. This is XSA-528 / CVE-2026-100082 Fixes: db6fbc106786 ("xen/blkback: make st_ statistics per ring") Reported-by: Yehyeong Lee Signed-off-by: Juergen Gross Acked-by: Roger Pau Monné --- drivers/block/xen-blkback/common.h | 2 ++ drivers/block/xen-blkback/xenbus.c | 30 ++++++++++++++++++++++++------ 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/drivers/block/xen-blkback/common.h b/drivers/block/xen-blkback/common.h index b427d54bc120..f5890b3f1df9 100644 --- a/drivers/block/xen-blkback/common.h +++ b/drivers/block/xen-blkback/common.h @@ -318,8 +318,10 @@ struct xen_blkif { unsigned int nr_ring_pages; bool multi_ref; /* All rings for this device. */ + rwlock_t rings_lock; struct xen_blkif_ring *rings; unsigned int nr_rings; + unsigned long buffer_squeeze_end; }; diff --git a/drivers/block/xen-blkback/xenbus.c b/drivers/block/xen-blkback/xenbus.c index 4b865ef800b1..8d0ff8fcac22 100644 --- a/drivers/block/xen-blkback/xenbus.c +++ b/drivers/block/xen-blkback/xenbus.c @@ -127,14 +127,23 @@ static void xen_update_blkif_status(struct xen_blkif *blkif) return; } -static int xen_blkif_alloc_rings(struct xen_blkif *blkif) +static int xen_blkif_alloc_rings(struct xen_blkif *blkif, + unsigned int nr_rings) { unsigned int r; + struct xen_blkif_ring *rings; - blkif->rings = kzalloc_objs(struct xen_blkif_ring, blkif->nr_rings); - if (!blkif->rings) + rings = kzalloc_objs(struct xen_blkif_ring, nr_rings); + if (!rings) return -ENOMEM; + write_lock(&blkif->rings_lock); + + blkif->rings = rings; + blkif->nr_rings = nr_rings; + + write_unlock(&blkif->rings_lock); + for (r = 0; r < blkif->nr_rings; r++) { struct xen_blkif_ring *ring = &blkif->rings[r]; @@ -174,6 +183,7 @@ static struct xen_blkif *xen_blkif_alloc(domid_t domid) blkif->domid = domid; atomic_set(&blkif->refcnt, 1); init_completion(&blkif->drain_complete); + rwlock_init(&blkif->rings_lock); /* * Because freeing back to the cache may be deferred, it is not @@ -268,6 +278,7 @@ static int xen_blkif_disconnect(struct xen_blkif *blkif) struct pending_req *req, *n; unsigned int j, r; bool busy = false; + struct xen_blkif_ring *rings; for (r = 0; r < blkif->nr_rings; r++) { struct xen_blkif_ring *ring = &blkif->rings[r]; @@ -334,10 +345,16 @@ static int xen_blkif_disconnect(struct xen_blkif *blkif) * blkif->rings was allocated in connect_ring, so we should free it in * here. */ - kfree(blkif->rings); + write_lock(&blkif->rings_lock); + + rings = blkif->rings; blkif->rings = NULL; blkif->nr_rings = 0; + write_unlock(&blkif->rings_lock); + + kfree(rings); + return 0; } @@ -385,6 +402,7 @@ void xen_blkif_interface_fini(void) unsigned int i; \ unsigned long long result = 0; \ \ + read_lock(&blkif->rings_lock); \ if (!blkif->rings) \ goto out; \ \ @@ -395,6 +413,7 @@ void xen_blkif_interface_fini(void) } \ \ out: \ + read_unlock(&blkif->rings_lock); \ return sprintf(buf, format, result); \ } \ static DEVICE_ATTR(name, 0444, show_##name, NULL) @@ -1102,8 +1121,7 @@ static int connect_ring(struct backend_info *be) requested_num_queues, xenblk_max_queues); return -ENOSYS; } - blkif->nr_rings = requested_num_queues; - if (xen_blkif_alloc_rings(blkif)) + if (xen_blkif_alloc_rings(blkif, requested_num_queues)) return -ENOMEM; pr_info("%s: using %d queues, protocol %d (%s) %s\n", dev->nodename, -- 2.56.0