The xdp program may overwrite the inline virtio header. To ensure the
integrity of the virtio header, it is saved in a data structure that
wraps both the xdp_buff and the header before running the xdp program.

Signed-off-by: Liang Chen <liangchen.li...@gmail.com>
---
 drivers/net/virtio_net.c | 43 +++++++++++++++++++++++++++++-----------
 1 file changed, 31 insertions(+), 12 deletions(-)

diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index d7ce4a1011ea..b56828804e5f 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -349,6 +349,11 @@ struct virtio_net_common_hdr {
        };
 };
 
+struct virtnet_xdp_buff {
+       struct xdp_buff xdp;
+       struct virtio_net_common_hdr hdr;
+};
+
 static void virtnet_sq_free_unused_buf(struct virtqueue *vq, void *buf);
 
 static bool is_xdp_frame(void *ptr)
@@ -1199,9 +1204,10 @@ static struct sk_buff *receive_small_xdp(struct 
net_device *dev,
        unsigned int headroom = vi->hdr_len + header_offset;
        struct virtio_net_hdr_mrg_rxbuf *hdr = buf + header_offset;
        struct page *page = virt_to_head_page(buf);
+       struct virtnet_xdp_buff virtnet_xdp;
        struct page *xdp_page;
+       struct xdp_buff *xdp;
        unsigned int buflen;
-       struct xdp_buff xdp;
        struct sk_buff *skb;
        unsigned int metasize = 0;
        u32 act;
@@ -1233,17 +1239,23 @@ static struct sk_buff *receive_small_xdp(struct 
net_device *dev,
                page = xdp_page;
        }
 
-       xdp_init_buff(&xdp, buflen, &rq->xdp_rxq);
-       xdp_prepare_buff(&xdp, buf + VIRTNET_RX_PAD + vi->hdr_len,
+       xdp = &virtnet_xdp.xdp;
+       xdp_init_buff(xdp, buflen, &rq->xdp_rxq);
+       xdp_prepare_buff(xdp, buf + VIRTNET_RX_PAD + vi->hdr_len,
                         xdp_headroom, len, true);
 
-       act = virtnet_xdp_handler(xdp_prog, &xdp, dev, xdp_xmit, stats);
+       /* Copy out the virtio header, as it may be overwritten by the
+        * xdp program.
+        */
+       memcpy(&virtnet_xdp.hdr, hdr, vi->hdr_len);
+
+       act = virtnet_xdp_handler(xdp_prog, xdp, dev, xdp_xmit, stats);
 
        switch (act) {
        case XDP_PASS:
                /* Recalculate length in case bpf program changed it */
-               len = xdp.data_end - xdp.data;
-               metasize = xdp.data - xdp.data_meta;
+               len = xdp->data_end - xdp->data;
+               metasize = xdp->data - xdp->data_meta;
                break;
 
        case XDP_TX:
@@ -1254,7 +1266,7 @@ static struct sk_buff *receive_small_xdp(struct 
net_device *dev,
                goto err_xdp;
        }
 
-       skb = virtnet_build_skb(buf, buflen, xdp.data - buf, len);
+       skb = virtnet_build_skb(buf, buflen, xdp->data - buf, len);
        if (unlikely(!skb))
                goto err;
 
@@ -1591,10 +1603,11 @@ static struct sk_buff *receive_mergeable_xdp(struct 
net_device *dev,
        int num_buf = virtio16_to_cpu(vi->vdev, hdr->num_buffers);
        struct page *page = virt_to_head_page(buf);
        int offset = buf - page_address(page);
+       struct virtnet_xdp_buff virtnet_xdp;
        unsigned int xdp_frags_truesz = 0;
        struct sk_buff *head_skb;
        unsigned int frame_sz;
-       struct xdp_buff xdp;
+       struct xdp_buff *xdp;
        void *data;
        u32 act;
        int err;
@@ -1604,16 +1617,22 @@ static struct sk_buff *receive_mergeable_xdp(struct 
net_device *dev,
        if (unlikely(!data))
                goto err_xdp;
 
-       err = virtnet_build_xdp_buff_mrg(dev, vi, rq, &xdp, data, len, frame_sz,
+       xdp = &virtnet_xdp.xdp;
+       err = virtnet_build_xdp_buff_mrg(dev, vi, rq, xdp, data, len, frame_sz,
                                         &num_buf, &xdp_frags_truesz, stats);
        if (unlikely(err))
                goto err_xdp;
 
-       act = virtnet_xdp_handler(xdp_prog, &xdp, dev, xdp_xmit, stats);
+       /* Copy out the virtio header, as it may be overwritten by the
+        * xdp program.
+        */
+       memcpy(&virtnet_xdp.hdr, hdr, vi->hdr_len);
+
+       act = virtnet_xdp_handler(xdp_prog, xdp, dev, xdp_xmit, stats);
 
        switch (act) {
        case XDP_PASS:
-               head_skb = build_skb_from_xdp_buff(dev, vi, &xdp, 
xdp_frags_truesz);
+               head_skb = build_skb_from_xdp_buff(dev, vi, xdp, 
xdp_frags_truesz);
                if (unlikely(!head_skb))
                        break;
                return head_skb;
@@ -1626,7 +1645,7 @@ static struct sk_buff *receive_mergeable_xdp(struct 
net_device *dev,
                break;
        }
 
-       put_xdp_frags(&xdp);
+       put_xdp_frags(xdp);
 
 err_xdp:
        put_page(page);
-- 
2.40.1


Reply via email to