take care of truncations done by sk_filter() in kernel-net-tcp CVE-2016-8645
asked 2018-03-06 10:19:14 +0200
This post is a wiki. Anyone with karma >75 is welcome to improve it.
The TCP stack in the Linux kernel before 4.8.10 mishandles skb truncation, which allows local users to cause a denial of service (system crash) via a crafted application that makes sendto system calls, related to net/ipv4/tcp_ipv4.c and net/ipv6/tcp_ipv6.c.
Suitable Kernel-3.2 backport [bwh: Backported to 3.2: adjust context] is available.
Files affected: kernel-adaptation-sbj-3.4.108.20161101.1/include/net/tcp.h lines 974-978
kernel-adaptation-sbj-3.4.108.20161101.1/net/ipv4/tcp_ipv4.c lines 1656-1661; 1718-1724
kernel-adaptation-sbj-3.4.108.20161101.1/net/ipv6/tcp_ipv6.c lines 1480-1486; 1640-1646
Patch should look like:
tcp.h:
@@ -974,6 +974,7 @@ static inline int tcp_prequeue(struct sock *sk, struct sk_buff *skb)
return 1;
}
+ int tcp_filter(struct sock *sk, struct sk_buff *skb);
#undef STATE_TRACE
tcp_ipv4.c:
@@ -1656,6 +1656,21 @@ csum_err:
}
EXPORT_SYMBOL(tcp_v4_do_rcv);
+int tcp_filter(struct sock *sk, struct sk_buff *skb)
+{
+ struct tcphdr *th = (struct tcphdr *)skb->data;
+ unsigned int eaten = skb->len;
+ int err;
+
+ err = sk_filter_trim_cap(sk, skb, th->doff * 4);
+ if (!err) {
+ eaten -= skb->len;
+ TCP_SKB_CB(skb)->end_seq -= eaten;
+ }
+ return err;
+}
+EXPORT_SYMBOL(tcp_filter);
+
/*
* From tcp_input.c
*/
@@ -1718,8 +1733,10 @@ process:
goto discard_and_relse;
nf_reset(skb);
- if (sk_filter(sk, skb))
+ if (tcp_filter(sk, skb))
goto discard_and_relse;
+ th = (const struct tcphdr *)skb->data;
+ iph = ip_hdr(skb);
skb->dev = NULL;
tcp_ipv6.c:
@@ -1480,7 +1480,7 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
goto discard;
#endif
- if (sk_filter(sk, skb))
+ if (tcp_filter(sk, skb))
goto discard;
/*
@@ -1640,8 +1640,10 @@ process:
if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb))
goto discard_and_relse;
- if (sk_filter(sk, skb))
+ if (tcp_filter(sk, skb))
goto discard_and_relse;
+ th = (const struct tcphdr *)skb->data;
+ hdr = ipv6_hdr(skb);
skb->dev = NULL;