In xdp_linearize_page, when reading the following buffers from the ring,
we forget to check the received length with the true allocate size. This
can lead to an out-of-bound read. This commit adds that missing check.

Fixes: 4941d472bf95 ("virtio-net: do not reset during XDP set")
Signed-off-by: Bui Quang Minh <minhquangbu...@gmail.com>
---
 drivers/net/virtio_net.c | 27 ++++++++++++++++++++++-----
 1 file changed, 22 insertions(+), 5 deletions(-)

diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index e53ba600605a..2a130a3e50ac 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -1797,7 +1797,8 @@ static unsigned int virtnet_get_headroom(struct 
virtnet_info *vi)
  * across multiple buffers (num_buf > 1), and we make sure buffers
  * have enough headroom.
  */
-static struct page *xdp_linearize_page(struct receive_queue *rq,
+static struct page *xdp_linearize_page(struct net_device *dev,
+                                      struct receive_queue *rq,
                                       int *num_buf,
                                       struct page *p,
                                       int offset,
@@ -1818,17 +1819,33 @@ static struct page *xdp_linearize_page(struct 
receive_queue *rq,
        page_off += *len;
 
        while (--*num_buf) {
-               unsigned int buflen;
+               unsigned int headroom, tailroom, room;
+               unsigned int truesize, buflen;
                void *buf;
+               void *ctx;
                int off;
 
-               buf = virtnet_rq_get_buf(rq, &buflen, NULL);
+               buf = virtnet_rq_get_buf(rq, &buflen, &ctx);
                if (unlikely(!buf))
                        goto err_buf;
 
                p = virt_to_head_page(buf);
                off = buf - page_address(p);
 
+               truesize = mergeable_ctx_to_truesize(ctx);
+               headroom = mergeable_ctx_to_headroom(ctx);
+               tailroom = headroom ? sizeof(struct skb_shared_info) : 0;
+               room = SKB_DATA_ALIGN(headroom + tailroom);
+
+               if (unlikely(buflen > truesize - room)) {
+                       put_page(p);
+                       pr_debug("%s: rx error: len %u exceeds truesize %lu\n",
+                                dev->name, buflen,
+                                (unsigned long)(truesize - room));
+                       DEV_STATS_INC(dev, rx_length_errors);
+                       goto err_buf;
+               }
+
                /* guard against a misconfigured or uncooperative backend that
                 * is sending packet larger than the MTU.
                 */
@@ -1917,7 +1934,7 @@ static struct sk_buff *receive_small_xdp(struct 
net_device *dev,
                headroom = vi->hdr_len + header_offset;
                buflen = SKB_DATA_ALIGN(GOOD_PACKET_LEN + headroom) +
                        SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
-               xdp_page = xdp_linearize_page(rq, &num_buf, page,
+               xdp_page = xdp_linearize_page(dev, rq, &num_buf, page,
                                              offset, header_offset,
                                              &tlen);
                if (!xdp_page)
@@ -2252,7 +2269,7 @@ static void *mergeable_xdp_get_buf(struct virtnet_info 
*vi,
         */
        if (!xdp_prog->aux->xdp_has_frags) {
                /* linearize data for XDP */
-               xdp_page = xdp_linearize_page(rq, num_buf,
+               xdp_page = xdp_linearize_page(vi->dev, rq, num_buf,
                                              *page, offset,
                                              XDP_PACKET_HEADROOM,
                                              len);
-- 
2.43.0


Reply via email to