[RFC 4/6] net/memif: validate descriptor length in zero-copy mode
Stephen Hemminger
stephen at networkplumber.org
Sat Aug 29 01:10:24 CEST 2026
In zero-copy mode the receive buffers are the driver's own mbufs,
and the peer supplies the resulting length. That length needs to
be checked so that buggy/hostile peer doesn't crash server.
Validate the length against the buffer size advertised to the peer.
Read descriptor length once to avoid TOCTOU issues.
An invalid length means the peer is not honoring the contract on a
field whose buffer the driver owns, so nothing else in the ring can
be trusted. Drop the burst and disconnect, as is done for the other
invalid descriptor cases.
While here, fix a leak on the existing number-of-segments-overflow
path.
Bugzilla ID: 2018
Fixes: 43b815d88188 ("net/memif: support zero-copy slave")
Cc: stable at dpdk.org
Signed-off-by: Stephen Hemminger <stephen at networkplumber.org>
---
drivers/net/memif/rte_eth_memif.c | 43 +++++++++++++++++++++++++++----
1 file changed, 38 insertions(+), 5 deletions(-)
diff --git a/drivers/net/memif/rte_eth_memif.c b/drivers/net/memif/rte_eth_memif.c
index 10fa2c59cf..6f93eceecb 100644
--- a/drivers/net/memif/rte_eth_memif.c
+++ b/drivers/net/memif/rte_eth_memif.c
@@ -701,7 +701,11 @@ eth_memif_rx_zc(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts)
memif_ring_t *ring = memif_get_ring_from_queue(proc_private, mq);
uint16_t cur_slot, last_slot, n_slots, ring_size, mask, s0, head;
uint16_t n_rx_pkts = 0;
+ /* Buffer size advertised to the peer by the refill loop below. */
+ const uint16_t buf_size = rte_pktmbuf_data_room_size(mq->mempool) -
+ RTE_PKTMBUF_HEADROOM;
memif_desc_t *d0;
+ memif_desc_t desc;
struct rte_mbuf *mbuf, *mbuf_tail;
struct rte_mbuf *mbuf_head = NULL;
int ret;
@@ -753,14 +757,31 @@ eth_memif_rx_zc(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts)
rte_prefetch0(&ring->desc[(cur_slot + 1) & mask]);
mbuf->port = mq->in_port;
- rte_pktmbuf_data_len(mbuf) = d0->length;
- rte_pktmbuf_pkt_len(mbuf) = rte_pktmbuf_data_len(mbuf);
+ desc = memif_desc_read(d0);
+
+ /* The peer only supplies the length here */
+ if (unlikely(desc.length > buf_size)) {
+ memif_desc_error(mq, &desc, MEMIF_DESC_STATUS_ERR_DATA_TOO_BIG);
+ /* Consume the slot before discarding */
+ cur_slot++;
+ n_slots--;
+ goto discard;
+ }
- mq->n_bytes += rte_pktmbuf_data_len(mbuf);
+ rte_pktmbuf_data_len(mbuf) = desc.length;
+ rte_pktmbuf_pkt_len(mbuf) = desc.length;
+ if (mbuf != mbuf_head)
+ rte_pktmbuf_pkt_len(mbuf_head) += desc.length;
+
+ mq->n_bytes += desc.length;
cur_slot++;
n_slots--;
- if (d0->flags & MEMIF_DESC_FLAG_NEXT) {
+ if (desc.flags & MEMIF_DESC_FLAG_NEXT) {
+ if (unlikely(n_slots == 0)) {
+ mq->n_err++;
+ goto discard;
+ }
s0 = cur_slot & mask;
d0 = &ring->desc[s0];
mbuf_tail = mbuf;
@@ -768,7 +789,8 @@ eth_memif_rx_zc(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts)
ret = memif_pktmbuf_chain(mbuf_head, mbuf_tail, mbuf);
if (unlikely(ret < 0)) {
MIF_LOG(ERR, "number-of-segments-overflow");
- goto refill;
+ mq->n_err++;
+ goto discard;
}
goto next_slot;
}
@@ -778,6 +800,17 @@ eth_memif_rx_zc(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts)
}
mq->last_tail = cur_slot;
+ goto refill;
+
+discard:
+ /*
+ * The peer is buggy or hostile, remaining descriptors cannot be trusted.
+ * Drop the partially built packet and the slots not yet consumed.
+ */
+ rte_pktmbuf_free(mbuf_head);
+ while (n_slots--)
+ rte_pktmbuf_free_seg(mq->buffers[cur_slot++ & mask]);
+ mq->last_tail = cur_slot;
/* Supply server with new buffers */
refill:
--
2.53.0
More information about the stable
mailing list