All of lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Michael S. Tsirkin" <mst@redhat.com>
Cc: virtualization@lists.linux.dev, "Eugenio Perez" <eperezma@redhat.com>
Subject: Re: [PATCH net v6 4/5] net: resolve uncommon partial checksums at ingress
Date: Sat, 24 Oct 2026 15:09:50 +0000	[thread overview]
Message-ID: <sashiko-outbox-183770@kernel.org> (raw)
In-Reply-To: <bd468c7ea5468d6e3827a3c678eee15243615580.1792768096.git.mst@redhat.com>

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] net: virtio_net_hdr_tnl_to_skb() retains untrusted inner state and csum
--

commit fe9720049eff45b73b2666232bd03b5f28b7c81f
Author: Michael S. Tsirkin <mst@redhat.com>

net: resolve uncommon partial checksums at ingress

This commit validates virtio_net_hdr checksum offsets against parsed L3 and L4
headers on ingress, completing non-GSO checksums in software and dropping GSO
packets when the checksum metadata does not match a known TCP or UDP header.

> diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h
> index 013c6c7e2dea0..14120710d0753 100644
> --- a/include/linux/virtio_net.h
> +++ b/include/linux/virtio_net.h

[ ... ]

> @@ -352,8 +621,8 @@ virtio_net_hdr_tnl_to_skb(struct sk_buff *skb,
>  			return -EINVAL;
>  	}
>  
> -	skb_set_inner_protocol(skb, inner_isv6 ? htons(ETH_P_IPV6) :
> -						 htons(ETH_P_IP));
> +	inner_proto = inner_isv6 ? htons(ETH_P_IPV6) : htons(ETH_P_IP);
> +	skb_set_inner_protocol(skb, inner_proto);
>  	if (hdr->flags & VIRTIO_NET_HDR_F_UDP_TUNNEL_CSUM) {
>  		if (!tnl_csum_negotiated)
>  			return -EINVAL;
> @@ -368,7 +637,20 @@ virtio_net_hdr_tnl_to_skb(struct sk_buff *skb,
>  	skb->inner_mac_header = inner_nh + skb_headroom(skb);
>  	skb->transport_header = outer_th + skb_headroom(skb);
>  	skb->encapsulation = 1;
> -	return 0;
> +
> +	if (!skb_is_gso(skb))
> +		skb_shinfo(skb)->gso_type &= ~(SKB_GSO_UDP_TUNNEL_CSUM |
> +					       SKB_GSO_UDP_TUNNEL);
> +
> +	/*
> +	 * We know VIRTIO_NET_HDR_F_NEEDS_CSUM was set so __virtio_net_hdr_to_skb
> +	 * set CHECKSUM_PARTIAL.
> +	 */
> +	if (virtio_net_csum_partial_ok_at_offset(skb, gso_inner_type, inner_nh,
> +						 inner_proto))
> +		return 0;
> +
> +	return __virtio_net_resolve_csum_partial_mismatch(skb);
>  }

[Severity: High]
When a packet arrives through tun_get_user() -> tun_vnet_hdr_tnl_to_skb() or
virtnet_receive_done() -> virtio_net_hdr_tnl_to_skb() with
VIRTIO_NET_HDR_GSO_UDP_TUNNEL_IPV4 or VIRTIO_NET_HDR_GSO_UDP_TUNNEL_IPV6 and
a gso_size large enough that __virtio_net_hdr_to_skb() does not mark the skb
as GSO (skb->len - nh_off <= gso_size, so !skb_is_gso(skb)), can this handling
in virtio_net_hdr_tnl_to_skb() leave untrusted inner header state and partial
checksums active on non-GSO packets?

First, when !skb_is_gso(skb) and virtio_net_csum_partial_ok_at_offset()
returns false because the inner offsets or headers are invalid,
__virtio_net_resolve_csum_partial_mismatch() completes the checksum via
skb_checksum_help() and returns 1. However, virtio_net_hdr_tnl_to_skb() has
already set skb->encapsulation = 1, skb->inner_protocol,
skb->inner_transport_header, skb->inner_network_header, and
skb->inner_mac_header from the unvalidated vhdr->inner_nh_offset and
hdr->csum_start.

If that non-GSO packet is later forwarded into an IP tunnel,
iptunnel_handle_offloads() in net/ipv4/ip_tunnel_core.c checks:

	if (likely(!skb->encapsulation)) {
		skb_reset_inner_headers(skb);
		skb->encapsulation = 1;
	}

Because skb->encapsulation is already 1, skb_reset_inner_headers() is skipped
and ip_tunnel_xmit() / ip_md_tunnel_xmit() read skb_inner_network_header(skb)
at the unvalidated inner_nh offset. Drivers and stack paths that inspect
skb->encapsulation on non-GSO packets, such as mlx4_en_xmit() in
drivers/net/ethernet/mellanox/mlx4/en_tx.c:

	if (skb->encapsulation) {
		...
		ip.hdr = skb_inner_network_header(skb);
		proto = (ip.v4->version == 4) ? ip.v4->protocol :
						ip.v6->nexthdr;

as well as netif_skb_features(), i40e_features_check(), ice_features_check(),
udp_gro_receive(), xfrm_get_inner_ipproto(), geneve_rx(), skb_mpls_push(), and
bpf_skb_adjust_room(), will also act on the unvalidated inner header offsets.

Second, virtio_net_tnl_offsets_ok() only verifies that the outer L3 header is
followed by UDP at outer_th, while inner_nh is taken directly from the
untrusted vhdr->inner_nh_offset (requiring only inner_nh >= outer_th +
sizeof(struct udphdr)). On a non-GSO UDP packet (!skb_is_gso(skb)), can a
sender place fake IP and UDP headers at a chosen offset inner_nh inside the
UDP payload (or inside an inner Ethernet/VXLAN header) so that
virtio_net_csum_partial_ok_at_offset() succeeds and returns 0, keeping
CHECKSUM_PARTIAL at an arbitrary offset inside the UDP payload where later
checksum completion modifies the packet after protocol or firewall inspection?

Third, when !skb_is_gso(skb) and virtio_net_csum_partial_ok_at_offset()
returns true, clearing SKB_GSO_UDP_TUNNEL_CSUM drops the outer UDP tunnel
checksum request while leaving skb->ip_summed == CHECKSUM_PARTIAL pointing to
inner_th. If the packet is delivered locally to udp_rcv() -> udp4_csum_init(),
skb_csum_unnecessary() in include/linux/skbuff.h:

	return ((skb->ip_summed == CHECKSUM_UNNECESSARY) ||
		skb->csum_valid ||
		(skb->ip_summed == CHECKSUM_PARTIAL &&
		 skb_checksum_start_offset(skb) >= 0));

evaluates to true at the outer UDP layer while udp_unexpected_gso() is false,
skipping outer UDP checksum validation and leaving the inner checksum
uncompleted.

Should virtio_net_hdr_tnl_to_skb() reject or avoid setting tunnel
encapsulation and inner CHECKSUM_PARTIAL state when !skb_is_gso(skb), and only
populate skb->encapsulation and skb->inner_* after the inner headers have been
validated?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1792768096.git.mst@redhat.com?part=4

  parent reply	other threads:[~2026-10-24 15:09 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-23 15:09 [PATCH net v6 0/5] more virtio-net csum hardening Michael S. Tsirkin
2026-10-23 15:09 ` [PATCH net v6 1/5] net: account for ingress-resolved virtio net hdr state Michael S. Tsirkin
2026-10-24 15:09   ` sashiko-bot
2026-10-23 15:09 ` [PATCH net v6 2/5] um: vector: trim skbs before verifying the header Michael S. Tsirkin
2026-10-24 15:09   ` sashiko-bot
2026-10-23 15:09 ` [PATCH net v6 3/5] virtio_net: parse tunnel csum_start little-endian Michael S. Tsirkin
2026-10-24 15:09   ` sashiko-bot
2026-10-23 15:09 ` [PATCH net v6 4/5] net: resolve uncommon partial checksums at ingress Michael S. Tsirkin
2026-10-23 19:37   ` Willem de Bruijn
2026-10-23 23:46     ` Michael S. Tsirkin
2026-10-24 15:09   ` sashiko-bot [this message]
2026-10-23 15:09 ` [PATCH net v6 5/5] net: add a sysctl to opt out of virtio net hdr checksum validation Michael S. Tsirkin
2026-10-24 15:09   ` sashiko-bot
2026-10-23 18:07 ` [PATCH net v6 0/5] more virtio-net csum hardening Chris Arges
2026-10-24 15:42 ` Michael S. Tsirkin
2026-10-24 16:08   ` Willem de Bruijn
2026-10-24 16:20     ` Michael S. Tsirkin
2026-10-25 13:50 ` Michael S. Tsirkin
2026-10-28 22:43   ` Jakub Kicinski
2026-10-29  7:22     ` Michael S. Tsirkin

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=sashiko-outbox-183770@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=eperezma@redhat.com \
    --cc=mst@redhat.com \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=virtualization@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.