[PATCH v2 net 6/6] net: enetc: pad short XDP frames coming from devmap
From: Vladimir Oltean
Date: Mon Apr 06 2026 - 16:46:42 EST
Similar to the skb path issue explained in the previous change, ENETC
could end up transmitting short frames coming from XDP.
The way in which this could happen is a bit contrived, but it involves
XDP_REDIRECT from a veth interface pair.
As for enetc_xmit(), there are two separate limitations for the overall
FRM_LEN and for the head BUFF_LEN. For the head BUFF_LEN, we add a
direct restriction in enetc_xdp_xmit(), and for the overall FRM_LEN, we
introduce a xdp_frame_pad() best-effort generic helper which we call
from the same place.
This helper alters the frame, but that should be safe, because
ndo_xdp_xmit() is the hand-off function where the XDP frames become the
responsibility of the driver. AFAIU, struct xdp_frame doesn't have
multiple copies.
I say best-effort because xdp_frame_pad() can only expand the head
buffer of an XDP frame. It cannot expand the last fragment of a
multi-buffer XDP frame, because, unlike bpf_xdp_frags_increase_tail(),
it lacks access to the rxq->frag_size, aka the capacity of the chunk of
memory being pointed to by the fragment.
So, if the frame happens to be less than minimum Ethernet size, but
fragmented, callers of this function will have to drop it.
Fixes: 9d2b68cc108d ("net: enetc: add support for XDP_REDIRECT")
Signed-off-by: Vladimir Oltean <vladimir.oltean@xxxxxxx>
---
v1->v2:
- handle multi-buffer frames instead of being unaware of their
multi-buffer quality
- add separate restriction for BUFF_LEN
- increment drop counter
---
drivers/net/ethernet/freescale/enetc/enetc.c | 10 ++++++++-
include/net/xdp.h | 23 ++++++++++++++++++++
2 files changed, 32 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/freescale/enetc/enetc.c b/drivers/net/ethernet/freescale/enetc/enetc.c
index c70df45422e0..196b7828e2aa 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc.c
@@ -1816,9 +1816,17 @@ int enetc_xdp_xmit(struct net_device *ndev, int num_frames,
prefetchw(ENETC_TXBD(*tx_ring, tx_ring->next_to_use));
for (k = 0; k < num_frames; k++) {
+ struct xdp_frame *xdpf = frames[k];
+
+ if (unlikely(xdp_frame_pad(xdpf) ||
+ xdpf->len < ENETC_MIN_BUFF_SIZE)) {
+ tx_ring->stats.xdp_tx_drops++;
+ break;
+ }
+
xdp_tx_bd_cnt = enetc_xdp_frame_to_xdp_tx_swbd(tx_ring,
xdp_redirect_arr,
- frames[k]);
+ xdpf);
if (unlikely(xdp_tx_bd_cnt < 0)) {
tx_ring->stats.xdp_tx_drops++;
break;
diff --git a/include/net/xdp.h b/include/net/xdp.h
index aa742f413c35..276afc9aa21d 100644
--- a/include/net/xdp.h
+++ b/include/net/xdp.h
@@ -477,6 +477,29 @@ xdp_get_frame_len(const struct xdp_frame *xdpf)
return len;
}
+static inline int xdp_frame_pad(struct xdp_frame *xdpf)
+{
+ unsigned int total_len, pad;
+ void *sinfo;
+
+ total_len = xdp_get_frame_len(xdpf);
+ if (likely(total_len >= ETH_ZLEN))
+ return 0;
+
+ if (unlikely(xdp_frame_has_frags(xdpf)))
+ return -EOPNOTSUPP;
+
+ pad = ETH_ZLEN - total_len;
+ sinfo = xdp_get_shared_info_from_frame(xdpf);
+ if (unlikely(xdpf->data + xdpf->len + pad > sinfo))
+ return -ENOMEM;
+
+ memset(xdpf->data + xdpf->len, 0, pad);
+ xdpf->len += pad;
+
+ return 0;
+}
+
int __xdp_rxq_info_reg(struct xdp_rxq_info *xdp_rxq,
struct net_device *dev, u32 queue_index,
unsigned int napi_id, u32 frag_size);
--
2.43.0