Skip to content

Commit 9274124

Browse files
wdebruijdavem330
authored andcommitted
net: stricter validation of untrusted gso packets
Syzkaller again found a path to a kernel crash through bad gso input: a packet with transport header extending beyond skb_headlen(skb). Tighten validation at kernel entry: - Verify that the transport header lies within the linear section. To avoid pulling linux/tcp.h, verify just sizeof tcphdr. tcp_gso_segment will call pskb_may_pull (th->doff * 4) before use. - Match the gso_type against the ip_proto found by the flow dissector. Fixes: bfd5f4a ("packet: Add GSO/csum offload support.") Reported-by: syzbot <[email protected]> Signed-off-by: Willem de Bruijn <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 0cb7498 commit 9274124

File tree

1 file changed

+24
-2
lines changed

1 file changed

+24
-2
lines changed

include/linux/virtio_net.h

+24-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
#define _LINUX_VIRTIO_NET_H
44

55
#include <linux/if_vlan.h>
6+
#include <uapi/linux/tcp.h>
7+
#include <uapi/linux/udp.h>
68
#include <uapi/linux/virtio_net.h>
79

810
static inline int virtio_net_hdr_set_proto(struct sk_buff *skb,
@@ -28,17 +30,25 @@ static inline int virtio_net_hdr_to_skb(struct sk_buff *skb,
2830
bool little_endian)
2931
{
3032
unsigned int gso_type = 0;
33+
unsigned int thlen = 0;
34+
unsigned int ip_proto;
3135

3236
if (hdr->gso_type != VIRTIO_NET_HDR_GSO_NONE) {
3337
switch (hdr->gso_type & ~VIRTIO_NET_HDR_GSO_ECN) {
3438
case VIRTIO_NET_HDR_GSO_TCPV4:
3539
gso_type = SKB_GSO_TCPV4;
40+
ip_proto = IPPROTO_TCP;
41+
thlen = sizeof(struct tcphdr);
3642
break;
3743
case VIRTIO_NET_HDR_GSO_TCPV6:
3844
gso_type = SKB_GSO_TCPV6;
45+
ip_proto = IPPROTO_TCP;
46+
thlen = sizeof(struct tcphdr);
3947
break;
4048
case VIRTIO_NET_HDR_GSO_UDP:
4149
gso_type = SKB_GSO_UDP;
50+
ip_proto = IPPROTO_UDP;
51+
thlen = sizeof(struct udphdr);
4252
break;
4353
default:
4454
return -EINVAL;
@@ -57,16 +67,22 @@ static inline int virtio_net_hdr_to_skb(struct sk_buff *skb,
5767

5868
if (!skb_partial_csum_set(skb, start, off))
5969
return -EINVAL;
70+
71+
if (skb_transport_offset(skb) + thlen > skb_headlen(skb))
72+
return -EINVAL;
6073
} else {
6174
/* gso packets without NEEDS_CSUM do not set transport_offset.
6275
* probe and drop if does not match one of the above types.
6376
*/
6477
if (gso_type && skb->network_header) {
78+
struct flow_keys_basic keys;
79+
6580
if (!skb->protocol)
6681
virtio_net_hdr_set_proto(skb, hdr);
6782
retry:
68-
skb_probe_transport_header(skb);
69-
if (!skb_transport_header_was_set(skb)) {
83+
if (!skb_flow_dissect_flow_keys_basic(NULL, skb, &keys,
84+
NULL, 0, 0, 0,
85+
0)) {
7086
/* UFO does not specify ipv4 or 6: try both */
7187
if (gso_type & SKB_GSO_UDP &&
7288
skb->protocol == htons(ETH_P_IP)) {
@@ -75,6 +91,12 @@ static inline int virtio_net_hdr_to_skb(struct sk_buff *skb,
7591
}
7692
return -EINVAL;
7793
}
94+
95+
if (keys.control.thoff + thlen > skb_headlen(skb) ||
96+
keys.basic.ip_proto != ip_proto)
97+
return -EINVAL;
98+
99+
skb_set_transport_header(skb, keys.control.thoff);
78100
}
79101
}
80102

0 commit comments

Comments
 (0)