Juergen Gross 435a43
Patch-mainline: v5.16-rc2
Juergen Gross 435a43
Git-commit: cf9acc90c80ecbee00334aa85d92f4e74014bcff
Juergen Gross 435a43
References: git-fixes
Juergen Gross 435a43
From: Jonathan Davies <jonathan.davies@nutanix.com>
Juergen Gross 435a43
Date: Tue, 16 Nov 2021 17:42:42 +0000
Juergen Gross 435a43
Subject: [PATCH] net: virtio_net_hdr_to_skb: count transport header in UFO
Juergen Gross 435a43
Juergen Gross 435a43
virtio_net_hdr_to_skb does not set the skb's gso_size and gso_type
Juergen Gross 435a43
correctly for UFO packets received via virtio-net that are a little over
Juergen Gross 435a43
the GSO size. This can lead to problems elsewhere in the networking
Juergen Gross 435a43
stack, e.g. ovs_vport_send dropping over-sized packets if gso_size is
Juergen Gross 435a43
not set.
Juergen Gross 435a43
Juergen Gross 435a43
This is due to the comparison
Juergen Gross 435a43
Juergen Gross 435a43
  if (skb->len - p_off > gso_size)
Juergen Gross 435a43
Juergen Gross 435a43
not properly accounting for the transport layer header.
Juergen Gross 435a43
Juergen Gross 435a43
p_off includes the size of the transport layer header (thlen), so
Juergen Gross 435a43
skb->len - p_off is the size of the TCP/UDP payload.
Juergen Gross 435a43
Juergen Gross 435a43
gso_size is read from the virtio-net header. For UFO, fragmentation
Juergen Gross 435a43
happens at the IP level so does not need to include the UDP header.
Juergen Gross 435a43
Juergen Gross 435a43
Hence the calculation could be comparing a TCP/UDP payload length with
Juergen Gross 435a43
an IP payload length, causing legitimate virtio-net packets to have
Juergen Gross 435a43
lack gso_type/gso_size information.
Juergen Gross 435a43
Juergen Gross 435a43
Example: a UDP packet with payload size 1473 has IP payload size 1481.
Juergen Gross 435a43
If the guest used UFO, it is not fragmented and the virtio-net header's
Juergen Gross 435a43
flags indicate that it is a GSO frame (VIRTIO_NET_HDR_GSO_UDP), with
Juergen Gross 435a43
gso_size = 1480 for an MTU of 1500.  skb->len will be 1515 and p_off
Juergen Gross 435a43
will be 42, so skb->len - p_off = 1473.  Hence the comparison fails, and
Juergen Gross 435a43
shinfo->gso_size and gso_type are not set as they should be.
Juergen Gross 435a43
Juergen Gross 435a43
Instead, add the UDP header length before comparing to gso_size when
Juergen Gross 435a43
using UFO. In this way, it is the size of the IP payload that is
Juergen Gross 435a43
compared to gso_size.
Juergen Gross 435a43
Juergen Gross 435a43
Fixes: 6dd912f82680 ("net: check untrusted gso_size at kernel entry")
Juergen Gross 435a43
Signed-off-by: Jonathan Davies <jonathan.davies@nutanix.com>
Juergen Gross 435a43
Reviewed-by: Willem de Bruijn <willemb@google.com>
Juergen Gross 435a43
Signed-off-by: David S. Miller <davem@davemloft.net>
Juergen Gross 435a43
Signed-off-by: Juergen Gross <jgross@suse.com>
Juergen Gross 435a43
---
Juergen Gross 435a43
 include/linux/virtio_net.h | 7 ++++++-
Juergen Gross 435a43
 1 file changed, 6 insertions(+), 1 deletion(-)
Juergen Gross 435a43
Juergen Gross 435a43
diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h
Juergen Gross 435a43
index b465f8f3e554..04e87f4b9417 100644
Juergen Gross 435a43
--- a/include/linux/virtio_net.h
Juergen Gross 435a43
+++ b/include/linux/virtio_net.h
Juergen Gross 435a43
@@ -120,10 +120,15 @@ static inline int virtio_net_hdr_to_skb(struct sk_buff *skb,
Juergen Gross 435a43
 
Juergen Gross 435a43
 	if (hdr->gso_type != VIRTIO_NET_HDR_GSO_NONE) {
Juergen Gross 435a43
 		u16 gso_size = __virtio16_to_cpu(little_endian, hdr->gso_size);
Juergen Gross 435a43
+		unsigned int nh_off = p_off;
Juergen Gross 435a43
 		struct skb_shared_info *shinfo = skb_shinfo(skb);
Juergen Gross 435a43
 
Juergen Gross 435a43
+		/* UFO may not include transport header in gso_size. */
Juergen Gross 435a43
+		if (gso_type & SKB_GSO_UDP)
Juergen Gross 435a43
+			nh_off -= thlen;
Juergen Gross 435a43
+
Juergen Gross 435a43
 		/* Too small packets are not really GSO ones. */
Juergen Gross 435a43
-		if (skb->len - p_off > gso_size) {
Juergen Gross 435a43
+		if (skb->len - nh_off > gso_size) {
Juergen Gross 435a43
 			shinfo->gso_size = gso_size;
Juergen Gross 435a43
 			shinfo->gso_type = gso_type;
Juergen Gross 435a43
 
Juergen Gross 435a43
-- 
Juergen Gross 435a43
2.35.3
Juergen Gross 435a43