All of lore.kernel.org
 help / color / mirror / Atom feed
From: Yu Zhang <yuz08559@gmail.com>
To: "Michael S . Tsirkin" <mst@redhat.com>,
	"Jason Wang" <jasowang@redhat.com>,
	"Xuan Zhuo" <xuanzhuo@linux.alibaba.com>,
	"Eugenio Pérez" <eperezma@redhat.com>
Cc: virtualization@lists.linux.dev, kvm@vger.kernel.org,
	netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
	Yu Zhang <yuz08559@gmail.com>
Subject: [PATCH] vduse: reject non-coherent iotlb entries in the coherent fault path
Date: Fri,  7 Aug 2026 12:26:57 +1000	[thread overview]
Message-ID: <20260807022657.11318-1-yuz08559@gmail.com> (raw)

vduse_domain_get_coherent_page() turns an iotlb entry into a struct page
with pfn_to_page((map->addr + iova - map->start) >> PAGE_SHIFT) and takes
a reference on it, with no check that map->addr is a physical address.

That assumption only holds for entries created by the domain's own
coherent allocator (vduse_domain_alloc_coherent(), which stores
virt_to_phys()). VDUSE registers its vdpa device with use_va=true, so for
a use_va device vhost_vdpa_va_map() passes a userspace virtual address as
the map address, and vduse_domain_set_map() copies those entries verbatim
into the same domain->iotlb. Both kinds of entry share one tree, and the
mmap fault handler only splits them by iova < bounce_size, so a use_va
entry placed above bounce_size lands in the coherent path.

A userspace daemon can exploit this: bind the device to virtio_vdpa so
the coherent allocator mints an entry whose backing file is the domain's
anon inode, obtain the domain fd with VDUSE_IOTLB_GET_FD2, rebind to
vhost_vdpa without deleting the vdpa device (vdpa_dev_del() only calls
_vdpa_unregister_device(), so the domain and the fd survive), install a
VHOST_IOTLB_UPDATE over the same iova with an arbitrary uaddr, then fault
the domain mapping. The kernel computes pfn = uaddr >> PAGE_SHIFT from
the userspace value and hands the daemon a struct page for a pfn of its
choosing -- an out-of-bounds vmemmap access for a wild value:

  BUG: unable to handle page fault for address: ffffea8000000008
  RIP: vduse_domain_mmap_fault+0x100/0x330

or, for a value that resolves to a valid pfn, a read/write window onto an
arbitrary physical page (confirmed: a write through the domain mapping
lands on a page selected purely by the fabricated pfn).

Coherent entries are always registered with domain->file as their backing
file, while entries installed through a vhost IOTLB message carry the
mapped vma's file (vhost_vdpa_va_map() stores get_file(vma->vm_file)).
Reject any entry whose file is not domain->file before calling
pfn_to_page(); the fault then returns VM_FAULT_SIGBUS. Legitimate coherent
mappings are unaffected.

Fixes: 8c773d53fb7b ("vduse: Implement an MMU-based software IOTLB")
Signed-off-by: Yu Zhang <yuz08559@gmail.com>
---
 drivers/vdpa/vdpa_user/iova_domain.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/drivers/vdpa/vdpa_user/iova_domain.c b/drivers/vdpa/vdpa_user/iova_domain.c
index 4dc76c0..847f010 100644
--- a/drivers/vdpa/vdpa_user/iova_domain.c
+++ b/drivers/vdpa/vdpa_user/iova_domain.c
@@ -221,6 +221,7 @@ vduse_domain_get_coherent_page(struct vduse_iova_domain *domain, u64 iova)
 	u64 start = iova & PAGE_MASK;
 	u64 last = start + PAGE_SIZE - 1;
 	struct vhost_iotlb_map *map;
+	struct vdpa_map_file *map_file;
 	struct page *page = NULL;
 
 	spin_lock(&domain->iotlb_lock);
@@ -228,6 +229,17 @@ vduse_domain_get_coherent_page(struct vduse_iova_domain *domain, u64 iova)
 	if (!map)
 		goto out;
 
+	/*
+	 * Only coherent allocations made by this domain are backed by a real
+	 * struct page here: their map->addr is a physical address and their
+	 * backing file is the domain's own anon inode. Entries installed via a
+	 * vhost IOTLB message on a use_va device instead carry a userspace
+	 * virtual address in map->addr, and must never be fed to pfn_to_page().
+	 */
+	map_file = (struct vdpa_map_file *)map->opaque;
+	if (map_file->file != domain->file)
+		goto out;
+
 	page = pfn_to_page((map->addr + iova - map->start) >> PAGE_SHIFT);
 	get_page(page);
 out:
-- 
2.43.0


             reply	other threads:[~2026-08-07  2:27 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-07  2:26 Yu Zhang [this message]
2026-08-08  2:27 ` [PATCH] vduse: reject non-coherent iotlb entries in the coherent fault path sashiko-bot

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260807022657.11318-1-yuz08559@gmail.com \
    --to=yuz08559@gmail.com \
    --cc=eperezma@redhat.com \
    --cc=jasowang@redhat.com \
    --cc=kvm@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mst@redhat.com \
    --cc=netdev@vger.kernel.org \
    --cc=virtualization@lists.linux.dev \
    --cc=xuanzhuo@linux.alibaba.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.