__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 Cc: stable@vger.kernel.org Assisted-by: LLM Signed-off-by: Paulos Yibelo Acked-by: Michael S. Tsirkin --- 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);