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
next prev 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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox