Linux virtualization list
 help / color / mirror / Atom feed
* [PATCH] iommu/virtio: bound the probe-property walk to the probe buffer
@ 2026-10-08  9:42 Yehyeong Lee
  2026-10-08  9:56 ` sashiko-bot
  0 siblings, 1 reply; 4+ messages in thread
From: Yehyeong Lee @ 2026-10-08  9:42 UTC (permalink / raw)
  To: jpb, joro, will
  Cc: robin.murphy, eric.auger, mst, virtualization, iommu,
	linux-kernel, Yehyeong Lee, stable

viommu_probe_endpoint() walks the properties the device returns in the
PROBE request, advancing a cursor by each property's device-provided
length.  The loop only checks that the start of a property header lies
within probe_size, but viommu_add_resv_mem() then dereferences a full
struct virtio_iommu_probe_resv_mem, reading mem->start and mem->end,
before its own length check runs.  A property placed near the end of the
buffer, or a probe_size smaller than the property, makes that read run
past the end of the probe allocation.

A malicious or buggy device triggers this with a RESV_MEM property at the
tail of the buffer.  KASAN reports a slab-out-of-bounds read, and the
out-of-bounds bytes become the start and end of a reserved region that is
then exposed to userspace through
/sys/kernel/iommu_groups/*/reserved_regions:

[    0.894779] BUG: KASAN: slab-out-of-bounds in viommu_probe_device+0x8b4/0xb20
[    0.894788] Read of size 8 at addr ffff88800a2eba4c by task swapper/0/1
[    0.894791]
[    0.894794] CPU: 1 UID: 0 PID: 1 Comm: swapper/0 Not tainted 7.3.0-rc6-00063-g0c2669a9f4a1 #1 PREEMPT(lazy)
[    0.894800] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS rel-1.17.0-0-gb52ca86e094d-prebuilt.qemu.org 04/01/2014
[    0.894802] Call Trace:
[    0.894805]  <TASK>
[    0.894807]  dump_stack_lvl+0x66/0xa0
[    0.894821]  print_report+0xd0/0x630
[    0.894827]  ? viommu_probe_device+0x8b4/0xb20
[    0.894831]  ? __virt_addr_valid+0x209/0x3f0
[    0.894837]  ? viommu_probe_device+0x8b4/0xb20
[    0.894840]  kasan_report+0xe4/0x120
[    0.894844]  ? viommu_probe_device+0x8b4/0xb20
[    0.894849]  viommu_probe_device+0x8b4/0xb20
[    0.894855]  __iommu_probe_device+0x298/0x12f0
[    0.894860]  ? __pfx___iommu_probe_device+0x10/0x10
[    0.894863]  ? lockdep_hardirqs_on_prepare+0xdc/0x190
[    0.894869]  ? trace_hardirqs_on+0x18/0x160
[    0.894875]  ? __pfx_probe_iommu_group+0x10/0x10
[    0.894879]  probe_iommu_group+0x25/0x50
[    0.894882]  bus_for_each_dev+0x103/0x180
[    0.894887]  ? __pfx_bus_for_each_dev+0x10/0x10
[    0.894890]  ? iommu_device_register+0x195/0x680
[    0.894893]  ? lock_release+0xc6/0x280
[    0.894899]  iommu_device_register+0x1d7/0x680
[    0.894903]  ? __pfx_iommu_device_register+0x10/0x10
[    0.894907]  ? __asan_memset+0x23/0x50
[    0.894913]  viommu_probe+0x1001/0x1460
[    0.894919]  ? __pfx_viommu_probe+0x10/0x10
[    0.894924]  ? lock_acquire+0x18d/0x300
[    0.894929]  ? __pfx_viommu_event_handler+0x10/0x10
[    0.894933]  ? vp_modern_set_extended_features+0x85/0xb0
[    0.894938]  ? vp_set_status+0x20/0x70
[    0.894944]  virtio_dev_probe+0x569/0xbe0
[    0.894950]  ? kernfs_add_one+0xeb/0x7a0
[    0.894956]  ? __pfx_virtio_dev_probe+0x10/0x10
[    0.894961]  ? kernfs_create_link+0x169/0x230
[    0.894965]  ? kernfs_put+0x17/0x30
[    0.894970]  ? sysfs_do_create_link_sd+0x89/0x100
[    0.894976]  really_probe+0x1c3/0x6a0
[    0.894981]  __driver_probe_device+0x19e/0x3b0
[    0.894985]  ? trace_hardirqs_on+0x18/0x160
[    0.894990]  driver_probe_device+0x45/0xd0
[    0.894994]  __driver_attach+0x149/0x3e0
[    0.894998]  ? __pfx___driver_attach+0x10/0x10
[    0.895002]  bus_for_each_dev+0x103/0x180
[    0.895006]  ? __pfx_bus_for_each_dev+0x10/0x10
[    0.895008]  ? bus_add_driver+0x1d1/0x500
[    0.895012]  ? lock_release+0xc6/0x280
[    0.895018]  bus_add_driver+0x20b/0x500
[    0.895022]  driver_register+0x12f/0x450
[    0.895027]  ? __pfx_virtio_iommu_drv_init+0x10/0x10
[    0.895032]  do_one_initcall+0xc9/0x440
[    0.895037]  ? __pfx_do_one_initcall+0x10/0x10
[    0.895042]  ? __kmalloc_noprof+0x40d/0x680
[    0.895047]  ? kernel_init_freeable+0x385/0x8e0
[    0.895053]  kernel_init_freeable+0x4c3/0x8e0
[    0.895057]  ? __pfx_kernel_init+0x10/0x10
[    0.895063]  kernel_init+0x1f/0x1e0
[    0.895068]  ? _raw_spin_unlock_irq+0x23/0x40
[    0.895072]  ? __pfx_kernel_init+0x10/0x10
[    0.895077]  ret_from_fork+0x52e/0x780
[    0.895081]  ? __pfx_ret_from_fork+0x10/0x10
[    0.895085]  ? __switch_to+0x572/0xde0
[    0.895091]  ? __pfx_kernel_init+0x10/0x10
[    0.895096]  ret_from_fork_asm+0x1a/0x30
[    0.895103]  </TASK>
[    0.895104]
[    0.895105] Allocated by task 1:
[    0.895108]  kasan_save_stack+0x33/0x60
[    0.895111]  kasan_save_track+0x14/0x30
[    0.895114]  __kasan_kmalloc+0x8f/0xa0
[    0.895117]  __kmalloc_noprof+0x264/0x680
[    0.895120]  viommu_probe_device+0x2e3/0xb20
[    0.895123]  __iommu_probe_device+0x298/0x12f0
[    0.895126]  probe_iommu_group+0x25/0x50
[    0.895129]  bus_for_each_dev+0x103/0x180
[    0.895131]  iommu_device_register+0x1d7/0x680
[    0.895134]  viommu_probe+0x1001/0x1460
[    0.895137]  virtio_dev_probe+0x569/0xbe0
[    0.895141]  really_probe+0x1c3/0x6a0
[    0.895144]  __driver_probe_device+0x19e/0x3b0
[    0.895148]  driver_probe_device+0x45/0xd0
[    0.895151]  __driver_attach+0x149/0x3e0
[    0.895154]  bus_for_each_dev+0x103/0x180
[    0.895160]  bus_add_driver+0x20b/0x500
[    0.895163]  driver_register+0x12f/0x450
[    0.895166]  do_one_initcall+0xc9/0x440
[    0.895166]  kernel_init_freeable+0x4c3/0x8e0
[    0.895166]  kernel_init+0x1f/0x1e0
[    0.895166]  ret_from_fork+0x52e/0x780
[    0.895166]  ret_from_fork_asm+0x1a/0x30
[    0.895166]
[    0.895166] The buggy address belongs to the object at ffff88800a2eb800
[    0.895166]  which belongs to the cache kmalloc-1k of size 1024
[    0.895166] The buggy address is located 0 bytes to the right of
[    0.895166]  allocated 588-byte region [ffff88800a2eb800, ffff88800a2eba4c)
[    0.895166]
[    0.895166] The buggy address belongs to the physical page:
[    0.895166] page: refcount:0 mapcount:0 mapping:0000000000000000 index:0xffff88800a2ee000 pfn:0xa2e8
[    0.895166] head: order:3 mapcount:0 entire_mapcount:0 nr_pages_mapped:0 pincount:0
[    0.895166] flags: 0x100000000000240(workingset|head|node=0|zone=1)
[    0.895166] page_type: f5(slab)
[    0.895166] raw: 0100000000000240 ffff888008c41dc0 ffff888008c40888 ffff888008c40888
[    0.895166] raw: ffff88800a2ee000 000000000010000c 00000000f5000000 0000000000000000
[    0.895166] head: 0100000000000240 ffff888008c41dc0 ffff888008c40888 ffff888008c40888
[    0.895166] head: ffff88800a2ee000 000000000010000c 00000000f5000000 0000000000000000
[    0.895166] head: 0100000000000003 fffffffffffffe01 00000000ffffffff 00000000ffffffff
[    0.895166] head: 0000000000000000 0000000000000000 00000000ffffffff 0000000000000000
[    0.895166] page dumped because: kasan: bad access detected
[    0.895166]
[    0.895166] Memory state around the buggy address:
[    0.895166]  ffff88800a2eb900: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[    0.895166]  ffff88800a2eb980: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[    0.895166] >ffff88800a2eba00: 00 00 00 00 00 00 00 00 00 04 fc fc fc fc fc fc
[    0.895166]                                               ^
[    0.895166]  ffff88800a2eba80: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[    0.895166]  ffff88800a2ebb00: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[    0.895166] ==================================================================

Bound the walk so a property cannot extend past probe_size, and move the
length check in viommu_add_resv_mem() ahead of the dereference.  The
property length is device-controlled, so widen it so the addition cannot
wrap.

Fixes: 2a5a314874450d ("iommu/virtio: Add probe request")
Cc: stable@vger.kernel.org
Signed-off-by: Yehyeong Lee <yhlee@isslab.korea.ac.kr>
---
 drivers/iommu/virtio-iommu.c | 13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)

diff --git a/drivers/iommu/virtio-iommu.c b/drivers/iommu/virtio-iommu.c
index 587fc13197f12..dbc997a6e3568 100644
--- a/drivers/iommu/virtio-iommu.c
+++ b/drivers/iommu/virtio-iommu.c
@@ -489,6 +489,9 @@ static int viommu_add_resv_mem(struct viommu_endpoint *vdev,
 	struct iommu_resv_region *region = NULL, *next;
 	unsigned long prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
 
+	if (len < sizeof(*mem))
+		return -EINVAL;
+
 	start = start64 = le64_to_cpu(mem->start);
 	end = end64 = le64_to_cpu(mem->end);
 	size = end64 - start64 + 1;
@@ -497,9 +500,6 @@ static int viommu_add_resv_mem(struct viommu_endpoint *vdev,
 	if (start != start64 || end != end64 || size < end64 - start64)
 		return -EOVERFLOW;
 
-	if (len < sizeof(*mem))
-		return -EINVAL;
-
 	switch (mem->subtype) {
 	default:
 		dev_warn(vdev->dev, "unknown resv mem subtype 0x%x\n",
@@ -531,7 +531,8 @@ static int viommu_add_resv_mem(struct viommu_endpoint *vdev,
 static int viommu_probe_endpoint(struct viommu_dev *viommu, struct device *dev)
 {
 	int ret;
-	u16 type, len;
+	u16 type;
+	unsigned int len;
 	size_t cur = 0;
 	size_t probe_len;
 	struct virtio_iommu_req_probe *probe;
@@ -563,8 +564,10 @@ static int viommu_probe_endpoint(struct viommu_dev *viommu, struct device *dev)
 	type = le16_to_cpu(prop->type) & VIRTIO_IOMMU_PROBE_T_MASK;
 
 	while (type != VIRTIO_IOMMU_PROBE_T_NONE &&
-	       cur < viommu->probe_size) {
+	       cur + sizeof(*prop) <= viommu->probe_size) {
 		len = le16_to_cpu(prop->length) + sizeof(*prop);
+		if (len > viommu->probe_size - cur)
+			break;
 
 		switch (type) {
 		case VIRTIO_IOMMU_PROBE_T_RESV_MEM:
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-10-08 10:59 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-08  9:42 [PATCH] iommu/virtio: bound the probe-property walk to the probe buffer Yehyeong Lee
2026-10-08  9:56 ` sashiko-bot
2026-10-08 10:26   ` Yehyeong Lee
2026-10-08 10:59   ` Michael S. Tsirkin

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox