From: sashiko-bot@kernel.org
To: "Eric Dumazet" <edumazet@kernel.org>
Cc: virtualization@lists.linux.dev,
"Michael S. Tsirkin" <mst@redhat.com>,
"Eugenio Perez" <eperezma@redhat.com>
Subject: Re: [PATCH net v2 1/3] net: virtio: validate outer protocol of UDP tunnel GSO packets
Date: Thu, 05 Nov 2026 05:01:52 +0000 [thread overview]
Message-ID: <sashiko-outbox-198573@kernel.org> (raw)
In-Reply-To: <20261104050131.629967-2-edumazet@kernel.org>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] net: virtio: L2 check bypass in virtio_net_l3_start() via TUNSETLINK
--
commit 546d87e0158fb3d32de1c338228f7d9a8f02bb4e
Author: Eric Dumazet <edumazet@kernel.org>
net: virtio: validate outer protocol of UDP tunnel GSO packets
This commit validates the outer L2/L3/L4 headers of UDP tunnel GSO packets in
virtio_net_hdr_tnl_to_skb() so that frames carrying mismatched outer protocols
or offsets are rejected before GSO segmentation.
Link: https://lore.kernel.org/netdev/93d0e37c99711bebdf60c120af03010455c2f2e2.1793276116.git.mst@redhat.com/
> diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h
> index d6466f96cdd00..b9c8babb1fd73 100644
> --- a/include/linux/virtio_net.h
> +++ b/include/linux/virtio_net.h
[ ... ]
> +/* Where does the L3 header start, and what protocol is it?
> + * Handles Ethernet and VLAN headers; other devices use the
> + * network header offset the caller set up.
> + */
> +static inline bool
> +virtio_net_l3_start(struct sk_buff *skb, int *nhoff, __be16 *proto)
> +{
> + /* skb->dev should always be set here already,
> + * but let's not crash when it is not.
> + */
> + if (WARN_ON_ONCE(!skb->dev))
> + return false;
> +
> + if (skb->dev->type == ARPHRD_ETHER ||
> + skb->dev->type == ARPHRD_LOOPBACK) {
> + __be16 h_proto;
> +
> + if (!pskb_may_pull(skb, ETH_HLEN))
> + return false;
> + h_proto = eth_hdr(skb)->h_proto;
> + /* skb->protocol is a sender claim, and parts of the
> + * stack parse the packet per it: when it disagrees with
> + * the frame we do not know which view the packet will
> + * be parsed as, so do not validate.
> + */
> + if (skb->protocol && skb->protocol != h_proto)
> + return false;
> + *nhoff = ETH_HLEN;
> + *proto = h_proto;
> + if (eth_type_vlan(*proto)) {
> + *proto = __vlan_get_protocol(skb, *proto, nhoff);
> + if (!*proto)
> + return false;
> + }
> + } else {
> + const struct iphdr *iph;
> +
> + *nhoff = skb_network_offset(skb);
> + /* An unset network header shows up as an offset
> + * outside of the packet.
> + */
> + if (*nhoff < 0 || (unsigned int)*nhoff >= skb->len)
> + return false;
> + /* No L2 header to cross-check with: take the protocol
> + * from the IP version at the network header.
> + */
> + if (!pskb_may_pull(skb, *nhoff + 1))
> + return false;
> + iph = (const struct iphdr *)(skb->data + *nhoff);
> + if (iph->version == 4)
> + *proto = htons(ETH_P_IP);
> + else if (iph->version == 6)
> + *proto = htons(ETH_P_IPV6);
> + else
> + return false;
> + if (skb->protocol && skb->protocol != *proto)
> + return false;
> + }
> + return true;
> +}
[Severity: High]
Can a tap writer bypass the Ethernet header check in virtio_net_l3_start() by
changing tun->dev->type via the TUNSETLINK ioctl?
In drivers/net/tun.c, __tun_chr_ioctl() allows changing tun->dev->type while
the device is down without changing tun->flags:
drivers/net/tun.c:__tun_chr_ioctl() {
case TUNSETLINK:
...
tun->dev->type = (int) arg;
tun->dev->addr_len = tun_get_addr_len(tun->dev->type);
}
Meanwhile, tun_get_user() (and tun_xdp_one()) decides whether the frame starts
with an Ethernet header based on (tun->flags & TUN_TYPE_MASK) == IFF_TAP,
setting the network header to ETH_HLEN before calling
tun_vnet_hdr_tnl_to_skb(), and only populating skb->protocol via
eth_type_trans() afterwards:
drivers/net/tun.c:tun_get_user() {
switch (tun->flags & TUN_TYPE_MASK) {
...
case IFF_TAP:
...
skb_set_network_header(skb, ETH_HLEN);
break;
}
if (tun_vnet_hdr_tnl_to_skb(tun->flags, features, skb, &hdr)) {
...
}
if ((tun->flags & TUN_TYPE_MASK) == IFF_TAP)
skb->protocol = eth_type_trans(skb, tun->dev);
}
If an IFF_TAP device has tun->dev->type set to another type such as
ARPHRD_NONE via TUNSETLINK, virtio_net_l3_start() takes the else branch, reads
*nhoff = skb_network_offset(skb) (which is ETH_HLEN), derives *proto from the
IP version nibble at offset 14, and skips the skb->protocol check because
skb->protocol is still 0.
The Ethernet header at offset 0 is never inspected by virtio_net_l3_start(),
so eth_type_trans() will subsequently set skb->protocol from the unvalidated
eth_hdr(skb)->h_proto (for example ETH_P_NSH) on a packet with
SKB_GSO_UDP_TUNNEL set, triggering the same skb_under_panic() during GSO
segmentation.
Conversely, on an IFF_TUN device whose tun->dev->type is changed to
ARPHRD_ETHER via TUNSETLINK, virtio_net_l3_start() validates a 14-byte
Ethernet header at offset 0 and an outer IP header at offset 14, but
tun_get_user() does not call eth_type_trans() and resets skb->network_header
to offset 0, leaving the outer network header pointing at the fake Ethernet
header.
Should tun_get_user() and tun_xdp_one() set skb->protocol before calling
tun_vnet_hdr_tnl_to_skb(), or should virtio_net_l3_start() avoid relying on
skb->dev->type matching the caller's L2 framing?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261104050131.629967-1-edumazet@kernel.org?part=1
next prev parent reply other threads:[~2026-11-05 5:01 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-04 5:01 [PATCH net v2 0/3] net: virtio: validate outer protocol of UDP tunnel GSO packets Eric Dumazet
2026-11-04 5:01 ` [PATCH net v2 1/3] " Eric Dumazet
2026-11-04 15:32 ` Willem de Bruijn
2026-11-04 15:36 ` Michael S. Tsirkin
2026-11-05 5:01 ` sashiko-bot [this message]
2026-11-05 10:08 ` netdev-bot+sashiko
2026-11-05 11:52 ` Eric Dumazet
2026-11-04 5:01 ` [PATCH net v2 2/3] selftests: net: tuntap: fix UDP checksum length in build_udp_packet_csum() Eric Dumazet
2026-11-05 5:01 ` sashiko-bot
2026-11-04 5:01 ` [PATCH net v2 3/3] selftests: net: tun: test outer protocol of UDP tunnel GSO packets Eric Dumazet
2026-11-04 15:36 ` Willem de Bruijn
2026-11-05 5:01 ` sashiko-bot
2026-11-05 10:08 ` netdev-bot+sashiko
2026-11-05 11:54 ` Eric Dumazet
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-198573@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=edumazet@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