net/memif: validate input from connecting peer
cover net/memif: validate input from connecting peer
A fuzzing effort by Arthur Chan turned up a number of ways for a memif client to upset a memif server. Taken together they are not isolated bugs so much as one systematic gap, which this series addresses.
memif is asymmetric. On connect the server hands its region file descriptors to the peer, so a client can reasonably trust the server it connected to, but the server cannot trust the client. The shared rings and their descriptors stay writable by the peer for the life of the connection, and control channel messages arrive before anything has been validated. The driver was written as if both ends were cooperative: it turned client-supplied region sizes, ring geometry and buffer descriptors into pointers and lengths without checking them. That is what the fuzzer found, from several directions.
Patches 3 to 5 do the validation (Bugzilla 2010, 2011, 2012, 2013, 2018, 2019) and are marked for stable, as is the statistics fix in patch 2. Patch 6 adds a two-process testpmd connectivity test, which is also the harness an adversarial peer can be driven from. Patches 1, 2 and 7 are unrelated to the fuzzing and bundled because they touch the same driver.
The PMD has effectively been unmaintained for some time, which is why these have sat. A call for a new maintainer went out and Sriram Yagnaraman has volunteered; he has reviewed and tested this series.
Interoperability
----------------
These checks were written against the VPP plugin and libmemif so that a hardened DPDK server does not disconnect conforming clients. Two are deliberately looser than they could be:
- A missing F_SEAL_SHRINK on a region is a warning, not a rejection. libmemif seals region 0, but hugepage-backed regions cannot be sealed, and a memfd created without MFD_ALLOW_SEALING is indistinguishable from one whose owner chose not to seal. The fstat size check covers the same ground without breaking those clients.
- Ring offsets are only required to be 8-byte aligned, though implementations use 64 in practice, to avoid rejecting small-ring setups.
If anyone knows of a stricter guarantee that can be relied on here, I would rather tighten these.
Not fixed here
--------------
Some reports need agreement on the wire protocol rather than a local fix:
- Bugzilla 2014: hello advertises the driver-wide maximum ring count rather than the device's configured count. The obvious one-line fix does not work: hello is built from the control channel alone, and the device is not associated with it until init, which arrives after hello has been sent. This means changing when ring counts are negotiated.
- Bugzilla 2015: ring semantics differ from the VPP implementation.
- Bugzilla 2017: the trust model and the normative requirements for a conforming client are not written down anywhere. That amounts to specifying the protocol, better done with a maintainer to agree it.
Three more pre-existing ways for a client to upset a server turned up while writing this. All are structural rather than local, so they are listed here rather than tacked onto the end of the series:
- memif_disconnect() unmaps the regions while the data path may still be inside a burst function. The burst functions test the connected flag only on entry, so a client that disconnects under load can already fault the server. Fixing it means waiting for lcores to leave the burst functions, or deferring the unmap to a quiescent point.
- Control messages are dispatched on type alone, with no check of role or connection state. A client that sends init then hello makes the server run the client-only setup path. This wants an accept table by role and state in memif_msg_receive().
- Queues the client does not add keep the ring geometry from the previous connection: disconnect does not reset it and connect only walks the negotiated ring counts. An offset validated against the old region can then be used against a new, smaller one. Ties into 2014.
One limitation within the series: a secondary process that sees a bad descriptor counts the error but cannot tear the connection down, since only the primary owns the control channel. That needs an mp message.
Stephen Hemminger (7):
maintainers: update for memif driver net/memif: fix issues in statistics
net/memif: validate peer descriptors net/memif: validate control channel requests net/memif: validate descriptor length in zero-copy mode net/memif: add server/client connectivity test
doc: clarify memif secret is not access control
.ci/linux-build.sh | 1 + .mailmap | 1 + MAINTAINERS | 2 + devtools/test-memif.sh | 169 +++++++++++++ doc/guides/nics/memif.rst | 20 +- drivers/net/memif/memif_socket.c | 129 ++++++++-- drivers/net/memif/rte_eth_memif.c | 397 +++++++++++++++++++++++++----- drivers/net/memif/rte_eth_memif.h | 1 + 8 files changed, 635 insertions(+), 85 deletions(-) create mode 100755 devtools/test-memif.sh
Signature · 1 line
2.53.0
PATCH 1/7 maintainers: update for memif driver
Sriram has volunteered to be memif maintainer.
--- MAINTAINERS | 1 + 1 file changed, 1 insertion(+)
MAINTAINERS +1 -0
@@ -1160,6 +1160,7 @@ F: drivers/net/softnic/ F: doc/guides/nics/softnic.rst Memif PMD +M: Sriram Yagnaraman <[email protected]> M: Jakub Grajciar <[email protected]> F: drivers/net/memif/ F: doc/guides/nics/memif.rst
Signature · 1 line
2.53.0
PATCH 2/7 net/memif: fix issues in statistics
In reply to Stephen HemmingerThe statistics structure is already cleared in ethdev before calling PMD.
Statistics from all queues should be counted against overall packets; the constant RTE_ETHDEV_QUEUE_STAT_CNTRS is upper bound on the array of queue stats.
The counters were also keyed off the negotiated ring counts in pmd->run, which memif_disconnect() clears, so every counter read back as zero once the peer went away. Iterate over the configured queue counts instead, in both stats_get and stats_reset, so the totals survive a disconnect.
--- drivers/net/memif/rte_eth_memif.c | 44 +++++++++++-------------------- 1 file changed, 15 insertions(+), 29 deletions(-)
drivers/net/memif/rte_eth_memif.c +15 -29
@@ -1595,25 +1595,18 @@ static int memif_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats, struct eth_queue_stats *qstats) { - struct pmd_internals *pmd = dev->data->dev_private; struct memif_queue *mq; -int i; -uint8_t tmp, nq; - - stats->ipackets =0 ; - stats->ibytes =0 ; - stats->opackets =0 ; - stats->obytes =0 ; +unsigned int i; - tmp = (pmd->role ==MEMIF_ROLE_CLIENT ) ? pmd->run.num_s2c_rings : - pmd->run.num_c2s_rings; - nq = (tmp <RTE_ETHDEV_QUEUE_STAT_CNTRS ) ? tmp : -RTE_ETHDEV_QUEUE_STAT_CNTRS ; +/* +* Use the configured queue counts, not pmd->run, which memif_disconnect() +* clears; otherwise all counters would read zero once the peer is gone. +*/ /* RX stats */ -for (i =0 ; i < nq; i++) { +for (i =0 ; i < dev->data->nb_rx_queues; i++) { mq = dev->data->rx_queues[i]; -if (qstats !=NULL ) { +if (qstats !=NULL && i <RTE_ETHDEV_QUEUE_STAT_CNTRS ) { qstats->q_ipackets[i] = mq->n_pkts; qstats->q_ibytes[i] = mq->n_bytes; }
@@ -1622,15 +1615,10 @@ memif_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats, stats->ierrors += mq->n_err; } - tmp = (pmd->role ==MEMIF_ROLE_CLIENT ) ? pmd->run.num_c2s_rings : - pmd->run.num_s2c_rings; - nq = (tmp <RTE_ETHDEV_QUEUE_STAT_CNTRS ) ? tmp : -RTE_ETHDEV_QUEUE_STAT_CNTRS ; -/* TX stats */ -for (i =0 ; i < nq; i++) { +for (i =0 ; i < dev->data->nb_tx_queues; i++) { mq = dev->data->tx_queues[i]; -if (qstats !=NULL ) { +if (qstats !=NULL && i <RTE_ETHDEV_QUEUE_STAT_CNTRS ) { qstats->q_opackets[i] = mq->n_pkts; qstats->q_obytes[i] = mq->n_bytes; }
@@ -1643,20 +1631,18 @@ memif_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats,static int memif_stats_reset (struct rte_eth_dev *dev) { -struct pmd_internals *pmd = dev->data->dev_private; -int i;struct memif_queue *mq; +unsigned int i; -for (i =0 ; i < pmd->run.num_c2s_rings; i++) { - mq = (pmd->role ==MEMIF_ROLE_CLIENT ) ? dev->data->tx_queues[i] : - dev->data->rx_queues[i]; +/* Same as memif_stats_get(), pmd->run is cleared on disconnect. */ +for (i =0 ; i < dev->data->nb_rx_queues; i++) { + mq = dev->data->rx_queues[i]; mq->n_pkts =0 ; mq->n_bytes =0 ; mq->n_err =0 ; } -for (i =0 ; i < pmd->run.num_s2c_rings; i++) { - mq = (pmd->role ==MEMIF_ROLE_CLIENT ) ? dev->data->rx_queues[i] : - dev->data->tx_queues[i]; +for (i =0 ; i < dev->data->nb_tx_queues; i++) { + mq = dev->data->tx_queues[i]; mq->n_pkts =0 ; mq->n_bytes =0 ; mq->n_err =0 ;
Signature · 1 line
2.53.0
PATCH 3/7 net/memif: validate peer descriptors
In reply to Stephen HemmingerThe buffer descriptors in the shared memory ring are writable by the peer at any time, and a server can not trust its client.
Validation classifies a bad descriptor into the same categories used by the VPP memif plugin. The descriptor length is 32 bits on the wire, so it is validated at full width before being narrowed for use; otherwise a length such as 0x10040 would be truncated to 0x40 and accepted.
A zero length buffer on a server to client ring is also rejected. It makes no forward progress, so the transmit path would consume ring slots without copying anything.
If server gets a bad request it is logged and reported as error. Since buggy or hostile client is not useful, the connection is aborted to avoid later problems.
Only the primary process owns the control channel, so a secondary that sees a bad descriptor counts the error but can not tear the connection down; that waits until the primary sees one itself. Propagating it needs an mp message and is left for later.
Bugzilla ID: 2010
--- .mailmap | 1 + drivers/net/memif/rte_eth_memif.c | 304 +++++++++++++++++++++++++++--- drivers/net/memif/rte_eth_memif.h | 1 + 3 files changed, 279 insertions(+), 27 deletions(-)
.mailmap +1 -0
@@ -159,6 +159,7 @@ Arshdeep Kaur <[email protected]> Artem V. Andreev <[email protected]> Artemii Morozov <[email protected]> Artemy Kovalyov <[email protected]> +Arthur Chan <[email protected]> Artin Davari <[email protected]> Artur Rojek <[email protected]> Artur Trybula <[email protected]>
drivers/net/memif/rte_eth_memif.c +277 -27
439 lines not on this page · raw message
drivers/net/memif/rte_eth_memif.h +1 -0
@@ -98,6 +98,7 @@ struct pmd_internals {struct memif_control_channel *cc;/**< control channel */ rte_spinlock_t cc_lock;/**< control channel lock */ +RTE_ATOMIC (bool ) bad_desc;/**< peer supplied bad descriptor */ /* remote info */ char remote_name[RTE_DEV_NAME_MAX_LEN ];/**< remote app name */
Signature · 1 line
2.53.0
PATCH 4/7 net/memif: validate control channel requests
In reply to Stephen HemmingerThe server can not trust the connecting client, validate connection requests before acting on them.
Check the file passed with region request to make sure that the claimed region size is backed by the file. Mapping beyond the end of a file succeeds and only faults on access, so a client that overstates the size can otherwise fault the server with SIGBUS. This also underpins the data path descriptor checks, which bound each descriptor against the region size.
Warn when the region is not sealed against shrinking, but do not reject it. Requiring a seal would disconnect conforming clients:
VPP's hugepage backed regions can not be sealed, and a memfd created without MFD_ALLOW_SEALING can not be distinguished from one whose owner chose not to seal.
For an add ring request: require rings to be added in order exactly once so the ring counts can not exceed the number of configured queues, bound the ring size to the advertised maximum, reject unsupported private headers, and check that the referenced region exists and that the ring plus its descriptor table fits inside that region at a naturally aligned offset.
A passed file descriptor is also closed when the message that carried it does not take one. Only add region and add ring consume an fd, so without this a client could attach one to any other message type and exhaust the file descriptors of the server.
Bugzilla ID: 2011
Bugzilla ID: 2012
Bugzilla ID: 2013
Bugzilla ID: 2019
--- drivers/net/memif/memif_socket.c | 129 ++++++++++++++++++++++++++----- 1 file changed, 108 insertions(+), 21 deletions(-)
drivers/net/memif/memif_socket.c +108 -21
@@ -7,6 +7,7 @@#include <unistd.h> #include <sys/types.h> #include <sys/socket.h> +#include <sys/stat.h> #include <sys/ioctl.h> #include <errno.h>
@@ -262,6 +263,8 @@ memif_msg_receive_add_region(struct rte_eth_dev *dev, memif_msg_t *msg,struct pmd_process_private *proc_private = dev->process_private;memif_msg_add_region_t *ar = &msg->add_region;struct memif_region *r; +struct stat st; +int seals;if (fd <0 ) {memif_msg_enq_disconnect (pmd->cc,"Missing region fd" ,0 );
@@ -272,13 +275,32 @@ memif_msg_receive_add_region(struct rte_eth_dev *dev, memif_msg_t *msg, ar->index != proc_private->regions_num || proc_private->regions[ar->index] != NULL) {memif_msg_enq_disconnect (pmd->cc,"Invalid region index" ,0 ); -return -1 ; + goto error; + } + +/* The client is not trusted to describe the region it shares. */ +if (ar->size ==0 ||fstat (fd, &st) <0 || (uint64_t )st.st_size < ar->size) { +memif_msg_enq_disconnect (pmd->cc,"Invalid region size" ,0 ); + goto error; } +/* +* Prefer regions sealed against shrinking so the peer can not truncate +* the file after connect and fault the server. Sealing is not supported +* on all fd types (for example hugetlbfs). +*/ + seals =fcntl (fd,F_GET_SEALS ); +if (seals <0 ) +MIF_LOG (INFO ,"Port %u region %u fd does not support sealing" , + dev->data->port_id, ar->index); +else if ((seals &F_SEAL_SHRINK ) ==0 ) +MIF_LOG (NOTICE ,"Port %u region %u fd is not sealed against shrinking" , + dev->data->port_id, ar->index); + r =rte_zmalloc ("region" ,sizeof (struct memif_region ),0 );if (r ==NULL ) {memif_msg_enq_disconnect (pmd->cc,"Failed to alloc memif region." ,0 ); -return -ENOMEM ; + goto error; } r->fd = fd;
@@ -289,46 +311,92 @@ memif_msg_receive_add_region(struct rte_eth_dev *dev, memif_msg_t *msg, proc_private->regions_num++;return 0 ; +error: +close (fd); +return -1 ; }static int memif_msg_receive_add_ring (struct rte_eth_dev *dev,memif_msg_t *msg,int fd) {struct pmd_internals *pmd = dev->data->dev_private; +struct pmd_process_private *proc_private = dev->process_private;memif_msg_add_ring_t *ar = &msg->add_ring; +const struct memif_region *r;struct memif_queue *mq; +uint64_t ring_size;if (fd <0 ) {memif_msg_enq_disconnect (pmd->cc,"Missing interrupt fd" ,0 );return -1 ; } -/* check if we have enough queues */ +/* rings must be added in order, exactly once */ if (ar->flags &MEMIF_MSG_ADD_RING_FLAG_C2S ) { -if (ar->index >= pmd->cfg.num_c2s_rings) { +if (ar->index >= pmd->cfg.num_c2s_rings || + ar->index != pmd->run.num_c2s_rings) {memif_msg_enq_disconnect (pmd->cc,"Invalid ring index" ,0 ); -return -1 ; + goto error; } - pmd->run.num_c2s_rings++; }else { -if (ar->index >= pmd->cfg.num_s2c_rings) { +if (ar->index >= pmd->cfg.num_s2c_rings || + ar->index != pmd->run.num_s2c_rings) {memif_msg_enq_disconnect (pmd->cc,"Invalid ring index" ,0 ); -return -1 ; + goto error; } - pmd->run.num_s2c_rings++; + } + +if (ar->log2_ring_size >ETH_MEMIF_MAX_LOG2_RING_SIZE ) { +memif_msg_enq_disconnect (pmd->cc,"Invalid ring size" ,0 ); + goto error; + } + +/* private headers are not supported */ +if (ar->private_hdr_size !=0 ) { +memif_msg_enq_disconnect (pmd->cc,"Unsupported private header" ,0 ); + goto error; + } + +if (ar->region >= proc_private->regions_num || + proc_private->regions[ar->region] ==NULL ) { +memif_msg_enq_disconnect (pmd->cc,"Invalid region index" ,0 ); + goto error; + } + +/* +* The ring and its descriptors must lie inside the region. +* Require natural alignment of the ring for atomic load/store. +* Existing DPDK and VPP put it on cache line boundary. +*/ + r = proc_private->regions[ar->region]; + ring_size =sizeof (memif_ring_t) + +sizeof (memif_desc_t) * ((uint64_t )1 << ar->log2_ring_size); +if ((ar->offset & (sizeof (uint64_t ) -1 )) !=0 || + ar->offset + ring_size > r->region_size) { +memif_msg_enq_disconnect (pmd->cc,"Invalid ring offset" ,0 ); + goto error; } mq = (ar->flags &MEMIF_MSG_ADD_RING_FLAG_C2S ) ? dev->data->rx_queues[ar->index] : dev->data->tx_queues[ar->index]; +/* Takes ownership of the fd, so nothing to close after this point. */ if (rte_intr_fd_set (mq->intr_handle, fd)) -return -1 ; + goto error; + +if (ar->flags &MEMIF_MSG_ADD_RING_FLAG_C2S ) + pmd->run.num_c2s_rings++; +else + pmd->run.num_s2c_rings++; mq->log2_ring_size = ar->log2_ring_size; mq->region = ar->region; mq->ring_offset = ar->offset;return 0 ; +error: +close (fd); +return -1 ; } static int
@@ -658,17 +726,11 @@ memif_msg_receive(struct memif_control_channel *cc)return -1 ; size =recvmsg (rte_intr_fd_get (cc->intr_handle), &mh,0 ); -if (size !=sizeof (memif_msg_t)) { -MIF_LOG (DEBUG ,"Invalid message size = %zd" , size); -if (size >0 ) -/* 0 means end-of-file, negative size means error, -* don't send further disconnect message in such cases. -*/ -memif_msg_enq_disconnect (cc,"Invalid message size" ,0 ); -return -1 ; - } -MIF_LOG (DEBUG ,"Received msg type: %u." , msg.type); +/* +* Collect any passed fd first; a short message can still carry one, +* and it has to be closed on every path out of this function. +*/ cmsg =CMSG_FIRSTHDR (&mh);while (cmsg) {if (cmsg->cmsg_level ==SOL_SOCKET ) {
@@ -680,10 +742,23 @@ memif_msg_receive(struct memif_control_channel *cc) cmsg =CMSG_NXTHDR (&mh, cmsg); } +if (size !=sizeof (memif_msg_t)) { +MIF_LOG (DEBUG ,"Invalid message size = %zd" , size); +if (size >0 ) +/* 0 means end-of-file, negative size means error, +* don't send further disconnect message in such cases. +*/ +memif_msg_enq_disconnect (cc,"Invalid message size" ,0 ); + ret =-1 ; + goto exit; + } +MIF_LOG (DEBUG ,"Received msg type: %u." , msg.type); +if (cc->dev ==NULL && msg.type !=MEMIF_MSG_TYPE_INIT ) {MIF_LOG (DEBUG ,"Unexpected message." );memif_msg_enq_disconnect (cc,"Unexpected message" ,0 ); -return -1 ; + ret =-1 ; + goto exit; }/* get device from hash data */
@@ -736,7 +811,9 @@ memif_msg_receive(struct memif_control_channel *cc) goto exit;break ;case MEMIF_MSG_TYPE_ADD_REGION : +/* The handler owns the fd from here, on success and on error. */ ret =memif_msg_receive_add_region (cc->dev, &msg, afd); + afd =-1 ;if (ret <0 ) goto exit; ret =memif_msg_enq_ack (cc->dev);
@@ -744,7 +821,9 @@ memif_msg_receive(struct memif_control_channel *cc) goto exit;break ;case MEMIF_MSG_TYPE_ADD_RING : +/* The handler owns the fd from here, on success and on error. */ ret =memif_msg_receive_add_ring (cc->dev, &msg, afd); + afd =-1 ;if (ret <0 ) goto exit; ret =memif_msg_enq_ack (cc->dev);
@@ -774,6 +853,14 @@ memif_msg_receive(struct memif_control_channel *cc) } exit: +/* +* A peer can attach an fd to any message, but only add region and +* add ring take one. Close the rest, otherwise a client could +* exhaust the file descriptors of the server. +*/ +if (afd >=0 ) +close (afd); +return ret; }
Signature · 1 line
2.53.0
PATCH 5/7 net/memif: validate descriptor length in zero-copy mode
In reply to Stephen HemmingerIn 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.
This also fixes the packet length of chained zero-copy segments. memif_pktmbuf_chain() adds the tail data_len while it is still zero, so a multi-segment packet previously carried only the length of its first segment.
While here, fix a leak on the existing number-of-segments-overflow path.
Bugzilla ID: 2018
--- drivers/net/memif/rte_eth_memif.c | 49 ++++++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 7 deletions(-)
drivers/net/memif/rte_eth_memif.c +42 -7
@@ -711,7 +711,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;
@@ -763,14 +767,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;
@@ -778,7 +799,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; }
@@ -788,6 +810,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:
@@ -820,8 +853,9 @@ eth_memif_rx_zc(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts) d0->length =rte_pktmbuf_data_room_size (mq->mempool) -RTE_PKTMBUF_HEADROOM ; d0->region =1 ; +/* Use the constant, the peer can change d0->region at any time. */ d0->offset = rte_pktmbuf_mtod(mbuf, uint8_t *) - - (uint8_t *)proc_private->regions[d0->region]->addr; + (uint8_t *)proc_private->regions[1 ]->addr; } no_free_mbufs:/* The ring->head acts as a guard variable between Tx and Rx
@@ -1096,8 +1130,9 @@ memif_tx_one_zc(struct pmd_process_private *proc_private, struct memif_queue *mq mq->n_bytes +=rte_pktmbuf_data_len (mbuf);/* FIXME: get region index */ d0->region =1 ; +/* Use the constant, the peer can change d0->region at any time. */ d0->offset = rte_pktmbuf_mtod(mbuf, uint8_t *) - - (uint8_t *)proc_private->regions[d0->region]->addr; + (uint8_t *)proc_private->regions[1 ]->addr; d0->flags =0 ;/* check if buffer is chained */
Signature · 1 line
2.53.0
PATCH 6/7 net/memif: add server/client connectivity test
In reply to Stephen HemmingerAdd a black box test that launches a memif server and client as two separate testpmd processes connected over a shared socket, and checks that traffic flows in both directions with no descriptor validation errors. This exercises the peer request validation over a real connection without depending on any internal driver API, and provides the two-instance harness that an adversarial (fuzzing) peer can be driven from later.
Both instances forward what they receive, so the client to server and server to client rings both carry traffic and the descriptors the client writes are validated on both paths. The client seeds the first burst with "start tx_first", which has to be issued as a command since --tx-first can not be combined with interactive mode.
The test is run from the CI alongside test-null.sh. It skips (exit 77) when the memif driver was not built, as in the CI jobs restricted with -Denable_drivers=net/null, and when fewer than four cores are online since the two primary processes need two cores each.
Bugzilla ID: 2016
--- .ci/linux-build.sh | 1 + MAINTAINERS | 1 + devtools/test-memif.sh | 169 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 171 insertions(+) create mode 100755 devtools/test-memif.sh
.ci/linux-build.sh +1 -0
@@ -178,6 +178,7 @@ if [ -z "$cross_file" ]; then failed= configure_coredump devtools/test-null.sh || failed="true" + devtools/test-memif.sh || [ $? = 77 ] || failed="true" catch_coredump catch_ubsan DPDK:fast-tests build/meson-logs/testlog.txt check_traces
MAINTAINERS +1 -0
@@ -1165,6 +1165,7 @@ M: Jakub Grajciar <[email protected]> F: drivers/net/memif/ F: doc/guides/nics/memif.rst F: doc/guides/nics/features/memif.ini +F: devtools/test-memif.sh Crypto Drivers
devtools/test-memif.sh new file +169 -0
169 lines not on this page · raw message
Signature · 1 line
2.53.0
PATCH 7/7 doc: clarify memif secret is not access control
In reply to Stephen HemmingerThe secret option was described as a security option, which invites using it as one. It is sent in cleartext in the connection request, and when passed as a device argument it is visible to other local users in the process arguments.
Describe it as what it is, a check against connecting mismatched interfaces, and document what actually restricts access to an interface. By default the control socket is in the abstract namespace and has no filesystem entry to own or permission. Only with socket-abstract=no do file permissions and the owner-uid and owner-gid options apply.
--- doc/guides/nics/memif.rst | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-)
doc/guides/nics/memif.rst +19 -1
@@ -47,9 +47,27 @@ client. "owner-uid=1000", "Set socket listener owner uid. Only relevant to server with socket-abstract=no", "unchanged", "uid_t" "owner-gid=1000", "Set socket listener owner gid. Only relevant to server with socket-abstract=no", "unchanged", "gid_t" "mac=01:23:45:ab:cd:ef", "Mac address", "01:ab:23:cd:45:ef", "" - "secret=abc123", "Secret is an optional security option, which if specified, must be matched by peer", "", "string len 24" + "secret=abc123", "Optional identifier which, if specified, must be matched by peer", "", "string len 24" "zero-copy=yes", "Enable/disable zero-copy client mode. Only relevant to client, requires '--single-file-segments' eal argument", "no", "yes|no" +**Access control** + +Any process able to connect to the socket of a server interface is able to +reach its shared memory rings, so what restricts access to that socket is +the security boundary. + +By default the socket is in the abstract namespace (``socket-abstract=yes``). +An abstract socket has no filesystem entry. +Use a network namespace to restrict access to such an interface. + +With ``socket-abstract=no`` the socket is a filesystem object and normal +file permissions apply, together with the ``owner-uid`` and ``owner-gid`` +options. + +The ``secret`` option is *not* an access control mechanism. +It only guards against connecting mismatched interfaces by mistake, +for example where several interfaces share one socket. + **Connection establishment** In order to create memif connection, two memif interfaces, each in separate
Signature · 1 line
2.53.0
Re: [PATCH 7/7] doc: clarify memif secret is not access control
Quoted 12 lines · On Tue, 22 Sep 2026 12:40:58 -0700 Stephen Hemminger…
The secret option was described as a security option, which invites using it as one. It is sent in cleartext in the connection request, and when passed as a device argument it is visible to other local users in the process arguments.
Describe it as what it is, a check against connecting mismatched interfaces, and document what actually restricts access to an interface. By default the control socket is in the abstract namespace and has no filesystem entry to own or permission. Only with socket-abstract=no do file permissions and the owner-uid and owner-gid options apply.
Quoted 3 lines
Signed-off-by Stephen Hemminger <[email protected]>Tested-by Sriram Yagnaraman <[email protected]>---
Recheck-request: aws-unit-testing
Re: [PATCH 7/7] doc: clarify memif secret is not access control
In reply to Stephen HemmingerQuoted 12 lines · On Tue, 22 Sep 2026 12:40:58 -0700 Stephen Hemminger…
The secret option was described as a security option, which invites using it as one. It is sent in cleartext in the connection request, and when passed as a device argument it is visible to other local users in the process arguments.
Describe it as what it is, a check against connecting mismatched interfaces, and document what actually restricts access to an interface. By default the control socket is in the abstract namespace and has no filesystem entry to own or permission. Only with socket-abstract=no do file permissions and the owner-uid and owner-gid options apply.
Quoted 3 lines
Signed-off-by Stephen Hemminger <[email protected]>Tested-by Sriram Yagnaraman <[email protected]>---
Recheck-request: aws-unit-testing
Quoted 10 lines
-----Original Message-----
From: Stephen Hemminger <[email protected]>
Sent: Tuesday, 22 September 2026 21:41
To: [email protected]
Cc: Stephen Hemminger <[email protected]>; Arthur Chan <[email protected]>
Subject: [PATCH 0/7] net/memif: validate input from connecting peer
Quoted 4 lines
The PMD has effectively been unmaintained for some time, which is why these have sat. A call for a new maintainer went out and Sriram Yagnaraman has volunteered; he has reviewed and tested this series.
Thanks for the series, Stephen.
Stephen Hemminger
AppliedQuoted 8 lines · On Thu, 24 Sep 2026 11:24:08 +0000 Sriram Yagnaraman…
Quoted 8 lines
-----Original Message-----
From: Stephen Hemminger <[email protected]>
Sent: Tuesday, 22 September 2026 21:41
To: [email protected]
Cc: Stephen Hemminger <[email protected]>; Arthur Chan <[email protected]>
Subject: [PATCH 0/7] net/memif: validate input from connecting peer
Quoted 7 lines
Quoted 4 lines
The PMD has effectively been unmaintained for some time, which is why these have sat. A call for a new maintainer went out and Sriram Yagnaraman has volunteered; he has reviewed and tested this series.
Thanks for the series, Stephen.
Applied to next-net. Had to resolve minor conflict on the statistics handler.