DPDK-dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] vhost: fix null dereference in async packed dequeue
@ 2026-07-07 13:50 Anton Vanda
  2026-07-22 23:08 ` Stephen Hemminger
  0 siblings, 1 reply; 2+ messages in thread
From: Anton Vanda @ 2026-07-07 13:50 UTC (permalink / raw)
  To: Thomas Monjalon, Maxime Coquelin, Chenbo Xia, Yuan Wang,
	Cheng Jiang
  Cc: dev, stable, cheng1.jiang, Anton Vanda

In the batch path of the asynchronous packed ring dequeue, the address
of the virtio net header is obtained from vhost_iova_to_vva(), which
returns 0 when a guest-provided descriptor address cannot be fully
translated.  The batch check only validates that the descriptor address
is non-zero and that the length is consistent. A malicious or buggy
guest could therefore trigger a NULL pointer dereference and crash the
vhost application (denial of service).

Check the translation result and leave the batch fast path with an error
on failure, so the single-packet path handles the invalid descriptor, as
is already done for the non-batch async dequeue path.

Perform the header translation before the DMA iovec setup so that the
early return cannot leave the async iterator state partially updated.

Fixes: c2fa52bf1e5d ("vhost: add batch dequeue in async vhost packed ring")
Cc: stable@dpdk.org

Signed-off-by: Anton Vanda <avanda@ptsecurity.com>
---
 .mailmap               |  1 +
 lib/vhost/virtio_net.c | 26 +++++++++++++++++---------
 2 files changed, 18 insertions(+), 9 deletions(-)

diff --git a/.mailmap b/.mailmap
index 05a55c0bd6..9215581a29 100644
--- a/.mailmap
+++ b/.mailmap
@@ -141,6 +141,7 @@ Antara Ganesh Kolar <antara.ganesh.kolar@intel.com>
 Anthony Fee <anthonyx.fee@intel.com>
 Anthony Harivel <aharivel@redhat.com>
 Antonio Fischetti <antonio.fischetti@intel.com>
+Anton Vanda <avanda@ptsecurity.com>
 Anup Prabhu <aprabhu@marvell.com>
 Anupam Kapoor <anupam.kapoor@gmail.com>
 Anurag Mandal <anurag.mandal@intel.com>
diff --git a/lib/vhost/virtio_net.c b/lib/vhost/virtio_net.c
index 0658b81de5..6528c06ea4 100644
--- a/lib/vhost/virtio_net.c
+++ b/lib/vhost/virtio_net.c
@@ -4039,6 +4039,23 @@ virtio_dev_tx_async_packed_batch(struct virtio_net *dev,
 	vhost_for_each_try_unroll(i, 0, PACKED_BATCH_SIZE)
 		rte_prefetch0((void *)(uintptr_t)desc_addrs[i]);
 
+	if (virtio_net_with_host_offload(dev)) {
+		vhost_for_each_try_unroll(i, 0, PACKED_BATCH_SIZE) {
+			desc_vva = vhost_iova_to_vva(dev, vq, desc_addrs[i],
+						&lens[i], VHOST_ACCESS_RO);
+			/*
+			 * A malformed or unmapped guest descriptor makes the
+			 * IOVA translation fail (returns 0). Bail out of the
+			 * batch fast path so the single-packet path handles the
+			 * error, instead of dereferencing a NULL header.
+			 */
+			if (unlikely(!desc_vva))
+				return -1;
+			hdr = (struct virtio_net_hdr *)(uintptr_t)desc_vva;
+			pkts_info[slot_idx + i].nethdr = *hdr;
+		}
+	}
+
 	vhost_for_each_try_unroll(i, 0, PACKED_BATCH_SIZE) {
 		host_iova[i] = (void *)(uintptr_t)gpa_to_first_hpa(dev,
 			desc_addrs[i] + buf_offset, pkts[i]->pkt_len, &mapped_len[i]);
@@ -4053,15 +4070,6 @@ virtio_dev_tx_async_packed_batch(struct virtio_net *dev,
 		async->iter_idx++;
 	}
 
-	if (virtio_net_with_host_offload(dev)) {
-		vhost_for_each_try_unroll(i, 0, PACKED_BATCH_SIZE) {
-			desc_vva = vhost_iova_to_vva(dev, vq, desc_addrs[i],
-						&lens[i], VHOST_ACCESS_RO);
-			hdr = (struct virtio_net_hdr *)(uintptr_t)desc_vva;
-			pkts_info[slot_idx + i].nethdr = *hdr;
-		}
-	}
-
 	vq_inc_last_avail_packed(vq, PACKED_BATCH_SIZE);
 
 	vhost_async_shadow_dequeue_packed_batch(vq, ids);
-- 
2.51.0


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

* Re: [PATCH] vhost: fix null dereference in async packed dequeue
  2026-07-07 13:50 [PATCH] vhost: fix null dereference in async packed dequeue Anton Vanda
@ 2026-07-22 23:08 ` Stephen Hemminger
  0 siblings, 0 replies; 2+ messages in thread
From: Stephen Hemminger @ 2026-07-22 23:08 UTC (permalink / raw)
  To: Anton Vanda
  Cc: Thomas Monjalon, Maxime Coquelin, Chenbo Xia, Yuan Wang,
	Cheng Jiang, dev, stable, cheng1.jiang

On Tue, 7 Jul 2026 16:50:44 +0300
Anton Vanda <avanda@ptsecurity.com> wrote:

> In the batch path of the asynchronous packed ring dequeue, the address
> of the virtio net header is obtained from vhost_iova_to_vva(), which
> returns 0 when a guest-provided descriptor address cannot be fully
> translated.  The batch check only validates that the descriptor address
> is non-zero and that the length is consistent. A malicious or buggy
> guest could therefore trigger a NULL pointer dereference and crash the
> vhost application (denial of service).
> 
> Check the translation result and leave the batch fast path with an error
> on failure, so the single-packet path handles the invalid descriptor, as
> is already done for the non-batch async dequeue path.
> 
> Perform the header translation before the DMA iovec setup so that the
> early return cannot leave the async iterator state partially updated.
> 
> Fixes: c2fa52bf1e5d ("vhost: add batch dequeue in async vhost packed ring")
> Cc: stable@dpdk.org
> 
> Signed-off-by: Anton Vanda <avanda@ptsecurity.com>
> ---

Since vhost is so security sensitive asked for more detailed AI review (Claude Fable).
It found some issues that need addressing before merge.


Review: [PATCH] vhost: fix null dereference in async packed dequeue

Errors
------

1. The check is incomplete: a non-zero return from vhost_iova_to_vva()
   does not mean the header is fully mapped. The function shrinks *len
   to the contiguously mapped length and returns a valid VVA whenever
   the start address falls inside a region (rte_vhost_va_from_guest_pa
   caps *len at region end; the IOTLB path does the same per entry).
   A guest that places a descriptor in the last 1-9 bytes of a memory
   region gets desc_vva != 0 with lens[i] < sizeof(struct virtio_net_hdr),
   and *hdr then reads past the end of the region mmap -- the same
   guest-triggerable crash class this patch is closing.

   Compare the other two paths:
   - single-packet: copy_vnet_hdr_from_desc() assembles a header that
     spans mappings from buf_vec chunks (virtio_net.c:2922).
   - sync batch: virtio_dev_tx_batch_packed_check() rejects partial
     mappings via lens[i] != descs[...].len after translation.

   Suggested fix -- translate only the header and verify coverage:

	uint64_t hdr_len = sizeof(struct virtio_net_hdr);

	desc_vva = vhost_iova_to_vva(dev, vq, desc_addrs[i],
				&hdr_len, VHOST_ACCESS_RO);
	if (unlikely(!desc_vva ||
		     hdr_len < sizeof(struct virtio_net_hdr)))
		return -1;

   Using a local length also stops clobbering lens[i]. That is harmless
   today (lens[] is dead after this point in the function) but fragile
   against future reordering.

TOCTOU assessment (requested)
-----------------------------

No TOCTOU introduced by this patch:

- No re-reads of the descriptor ring: the translation uses the
  desc_addrs[]/lens[] snapshots taken in
  vhost_async_tx_batch_packed_check(); guest-shared memory is not
  consulted again for validation.
- The header is consumed via a single struct copy into
  pkts_info[slot_idx + i].nethdr; the completion path parses that
  snapshot, so concurrent guest writes to the header cannot produce
  inconsistent offload parsing. The store escapes to the heap, so the
  compiler cannot elide or delay the copy -- the rte_compiler_barrier()
  that desc_to_mbuf() needs for its stack-local tmp_hdr is not needed
  here.
- Moving the block before the iovec setup does not open a check/use
  window: the same snapshot address feeds both the header translation
  and gpa_to_first_hpa().
- The early return leaves no partial state: async->iter_idx and the
  iovec array are untouched, last_avail_idx and the shadow ring are not
  advanced, and the caller falls through to the single-packet path,
  whose desc_to_mbuf() rewrites nethdr for any lanes the failed batch
  already filled. The reordering rationale in the commit message is
  correct.

Info
----

1. Same function, same threat model, pre-existing: host_iova[i] from
   gpa_to_first_hpa() is never checked; on translation failure
   rte_dma_copy() is issued with source IOVA 0, and a short mapped_len[i]
   silently truncates the copy. Worth a follow-up patch in this series
   or after.

2. .mailmap uses C-locale ordering ("Anup Prabhu" before
   "Anupam Kapoor"), so "Anton Vanda" sorts before "Antonio Fischetti"
   -- the new entry belongs one line up, after "Anthony Harivel".


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

end of thread, other threads:[~2026-07-22 23:08 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-07 13:50 [PATCH] vhost: fix null dereference in async packed dequeue Anton Vanda
2026-07-22 23:08 ` Stephen Hemminger

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