__virtio_net_hdr_to_skb() rejects a CHECKSUM_PARTIAL start smaller than an
estimated minimum network-header length. The comparison currently uses the
offset from skb->data rather than the offset from skb_network_header().

For an AF_PACKET frame, skb->data can still point at the Ethernet header
while skb_network_header() points past nested link-layer headers. A
checksum start at the network header can therefore pass, then target byte
zero after those headers are removed.

This does not require a virtual-machine guest. A TUN device with
virtio-net header support can supply the same checksum metadata.

Keep the existing data-relative lower bound and also require checksum
start to follow the estimated minimum relative to skb_network_header().

Fixes: 49d14b54a527 ("net: test for not too small csum_start in 
virtio_net_hdr_to_skb()")
Reported-by: Paulos Yibelo <[email protected]>
Cc: [email protected]
Assisted-by: LLM
Signed-off-by: Paulos Yibelo <[email protected]>
Acked-by: Michael S. Tsirkin <[email protected]>
---
Changes in v4:
- State explicitly that a TUN device is sufficient and no guest is required,
  as noted by Michael S. Tsirkin. No code changes.

Changes in v3:
- Keep the network-relative comparison on one line for readability, as
  requested by David Ahern.

Changes in v2:
- Make nh_min_len an int and remove the casts, as suggested by Michael S.
  Tsirkin.

 include/linux/virtio_net.h | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h
index c381b91..a95ad46 100644
--- a/include/linux/virtio_net.h
+++ b/include/linux/virtio_net.h
@@ -52,7 +52,7 @@ static inline int __virtio_net_hdr_to_skb(struct sk_buff *skb,
                                          const struct virtio_net_hdr *hdr,
                                          bool little_endian, u8 hdr_gso_type)
 {
-       unsigned int nh_min_len = sizeof(struct iphdr);
+       int nh_min_len = sizeof(struct iphdr);
        unsigned int gso_type = 0;
        unsigned int thlen = 0;
        unsigned int p_off = 0;
@@ -104,7 +104,8 @@ static inline int __virtio_net_hdr_to_skb(struct sk_buff 
*skb,
 
                if (!skb_partial_csum_set(skb, start, off))
                        return -EINVAL;
-               if (skb_transport_offset(skb) < nh_min_len)
+               if (skb_transport_offset(skb) < nh_min_len ||
+                   skb_transport_offset(skb) - skb_network_offset(skb) < 
nh_min_len)
                        return -EINVAL;
 
                nh_min_len = skb_transport_offset(skb);

Reply via email to