[linux] 01/05: net: Limit sk_filter trim to payload

debian-kernel at lists.debian.org debian-kernel at lists.debian.org
Sat Dec 31 19:38:24 UTC 2016


This is an automated email from the git hooks/post-receive script.

benh pushed a commit to branch wheezy-security
in repository linux.

commit 6927ee21eb1965b0ae823a2b592e3b64883fbb37
Author: Ben Hutchings <ben at decadent.org.uk>
Date:   Thu Dec 29 01:32:23 2016 +0000

    net: Limit sk_filter trim to payload
    
    rose, dccp and tcp all break if a socket filter truncates packet
    headers.  For tcp this was assigned CVE-2016-8645.
    
    Add the __sock_queue_rcv_skb() function which this depends on.
    
    Fix the ABI change caused by changing functions from exported to
    inline.
---
 debian/changelog                                   |  3 +
 .../all/dccp-limit-sk_filter-trim-to-payload.patch | 90 ++++++++++++++++++++
 .../all/rose-limit-sk_filter-trim-to-payload.patch | 94 +++++++++++++++++++++
 ...ake-care-of-truncations-done-by-sk_filter.patch | 98 ++++++++++++++++++++++
 .../net-fix-abi-change-for-sk_filter-changes.patch | 68 +++++++++++++++
 .../all/net-add-__sock_queue_rcv_skb.patch         | 63 ++++++++++++++
 debian/patches/series                              |  5 ++
 7 files changed, 421 insertions(+)

diff --git a/debian/changelog b/debian/changelog
index 91fd35e..5e406a7 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -116,6 +116,9 @@ linux (3.2.84-1) UNRELEASED; urgency=medium
   * [x86] Fix potential infoleak in older kernels (CVE-2016-9178)
   * [x86] KVM: drop error recovery in em_jmp_far and em_ret_far (CVE-2016-9756)
   * ALSA: pcm : Call kill_fasync() in stream lock (CVE-2016-9794)
+  * net: Add __sock_queue_rcv_skb()
+  * rose,dccp: limit sk_filter trim to payload
+  * tcp: take care of truncations done by sk_filter() (CVE-2016-8645)
 
  -- Ben Hutchings <ben at decadent.org.uk>  Mon, 28 Nov 2016 18:43:52 +0000
 
diff --git a/debian/patches/bugfix/all/dccp-limit-sk_filter-trim-to-payload.patch b/debian/patches/bugfix/all/dccp-limit-sk_filter-trim-to-payload.patch
new file mode 100644
index 0000000..695ff37
--- /dev/null
+++ b/debian/patches/bugfix/all/dccp-limit-sk_filter-trim-to-payload.patch
@@ -0,0 +1,90 @@
+From: Willem de Bruijn <willemb at google.com>
+Date: Tue, 12 Jul 2016 18:18:57 -0400
+Subject: dccp: limit sk_filter trim to payload
+Origin: https://git.kernel.org/linus/4f0c40d94461cfd23893a17335b2ab78ecb333c8
+
+Dccp verifies packet integrity, including length, at initial rcv in
+dccp_invalid_packet, later pulls headers in dccp_enqueue_skb.
+
+A call to sk_filter in-between can cause __skb_pull to wrap skb->len.
+skb_copy_datagram_msg interprets this as a negative value, so
+(correctly) fails with EFAULT. The negative length is reported in
+ioctl SIOCINQ or possibly in a DCCP_WARN in dccp_close.
+
+Introduce an sk_receive_skb variant that caps how small a filter
+program can trim packets, and call this in dccp with the header
+length. Excessively trimmed packets are now processed normally and
+queued for reception as 0B payloads.
+
+Fixes: 7c657876b63c ("[DCCP]: Initial implementation")
+Signed-off-by: Willem de Bruijn <willemb at google.com>
+Acked-by: Daniel Borkmann <daniel at iogearbox.net>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+[bwh: Backported to 3.2: adjust context]
+---
+--- a/include/net/sock.h
++++ b/include/net/sock.h
+@@ -1268,8 +1268,13 @@ static inline void sock_put(struct sock
+ 		sk_free(sk);
+ }
+ 
+-extern int sk_receive_skb(struct sock *sk, struct sk_buff *skb,
+-			  const int nested);
++int __sk_receive_skb(struct sock *sk, struct sk_buff *skb, const int nested,
++		     unsigned int trim_cap);
++static inline int sk_receive_skb(struct sock *sk, struct sk_buff *skb,
++				 const int nested)
++{
++	return __sk_receive_skb(sk, skb, nested, 1);
++}
+ 
+ static inline void sk_tx_queue_set(struct sock *sk, int tx_queue)
+ {
+--- a/net/core/sock.c
++++ b/net/core/sock.c
+@@ -336,11 +336,12 @@ int sock_queue_rcv_skb(struct sock *sk,
+ }
+ EXPORT_SYMBOL(sock_queue_rcv_skb);
+ 
+-int sk_receive_skb(struct sock *sk, struct sk_buff *skb, const int nested)
++int __sk_receive_skb(struct sock *sk, struct sk_buff *skb,
++		     const int nested, unsigned int trim_cap)
+ {
+ 	int rc = NET_RX_SUCCESS;
+ 
+-	if (sk_filter(sk, skb))
++	if (sk_filter_trim_cap(sk, skb, trim_cap))
+ 		goto discard_and_relse;
+ 
+ 	skb->dev = NULL;
+@@ -376,7 +377,7 @@ discard_and_relse:
+ 	kfree_skb(skb);
+ 	goto out;
+ }
+-EXPORT_SYMBOL(sk_receive_skb);
++EXPORT_SYMBOL(__sk_receive_skb);
+ 
+ void sk_reset_txq(struct sock *sk)
+ {
+--- a/net/dccp/ipv4.c
++++ b/net/dccp/ipv4.c
+@@ -877,7 +877,7 @@ static int dccp_v4_rcv(struct sk_buff *s
+ 		goto discard_and_relse;
+ 	nf_reset(skb);
+ 
+-	return sk_receive_skb(sk, skb, 1);
++	return __sk_receive_skb(sk, skb, 1, dh->dccph_doff * 4);
+ 
+ no_dccp_socket:
+ 	if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
+--- a/net/dccp/ipv6.c
++++ b/net/dccp/ipv6.c
+@@ -817,7 +817,7 @@ static int dccp_v6_rcv(struct sk_buff *s
+ 	if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb))
+ 		goto discard_and_relse;
+ 
+-	return sk_receive_skb(sk, skb, 1) ? -1 : 0;
++	return __sk_receive_skb(sk, skb, 1, dh->dccph_doff * 4) ? -1 : 0;
+ 
+ no_dccp_socket:
+ 	if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb))
diff --git a/debian/patches/bugfix/all/rose-limit-sk_filter-trim-to-payload.patch b/debian/patches/bugfix/all/rose-limit-sk_filter-trim-to-payload.patch
new file mode 100644
index 0000000..d2fbb02
--- /dev/null
+++ b/debian/patches/bugfix/all/rose-limit-sk_filter-trim-to-payload.patch
@@ -0,0 +1,94 @@
+From: Willem de Bruijn <willemb at google.com>
+Date: Tue, 12 Jul 2016 18:18:56 -0400
+Subject: rose: limit sk_filter trim to payload
+Origin: https://git.kernel.org/linus/f4979fcea7fd36d8e2f556abef86f80e0d5af1ba
+
+Sockets can have a filter program attached that drops or trims
+incoming packets based on the filter program return value.
+
+Rose requires data packets to have at least ROSE_MIN_LEN bytes. It
+verifies this on arrival in rose_route_frame and unconditionally pulls
+the bytes in rose_recvmsg. The filter can trim packets to below this
+value in-between, causing pull to fail, leaving the partial header at
+the time of skb_copy_datagram_msg.
+
+Place a lower bound on the size to which sk_filter may trim packets
+by introducing sk_filter_trim_cap and call this for rose packets.
+
+Signed-off-by: Willem de Bruijn <willemb at google.com>
+Acked-by: Daniel Borkmann <daniel at iogearbox.net>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+[bwh: Backported to 3.2: adjust context]
+---
+ include/linux/filter.h |  6 +++++-
+ net/core/filter.c      | 10 +++++-----
+ net/rose/rose_in.c     |  3 ++-
+ 3 files changed, 12 insertions(+), 7 deletions(-)
+
+--- a/include/linux/filter.h
++++ b/include/linux/filter.h
+@@ -150,7 +150,11 @@ static inline unsigned int sk_filter_len
+ 	return fp->len * sizeof(struct sock_filter) + sizeof(*fp);
+ }
+ 
+-extern int sk_filter(struct sock *sk, struct sk_buff *skb);
++int sk_filter_trim_cap(struct sock *sk, struct sk_buff *skb, unsigned int cap);
++static inline int sk_filter(struct sock *sk, struct sk_buff *skb)
++{
++	return sk_filter_trim_cap(sk, skb, 1);
++}
+ extern unsigned int sk_run_filter(const struct sk_buff *skb,
+ 				  const struct sock_filter *filter);
+ extern int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk);
+--- a/net/core/filter.c
++++ b/net/core/filter.c
+@@ -64,9 +64,10 @@ static inline void *load_pointer(const s
+ }
+ 
+ /**
+- *	sk_filter - run a packet through a socket filter
++ *	sk_filter_trim_cap - run a packet through a socket filter
+  *	@sk: sock associated with &sk_buff
+  *	@skb: buffer to filter
++ *	@cap: limit on how short the eBPF program may trim the packet
+  *
+  * Run the filter code and then cut skb->data to correct size returned by
+  * sk_run_filter. If pkt_len is 0 we toss packet. If skb->len is smaller
+@@ -75,7 +76,7 @@ static inline void *load_pointer(const s
+  * be accepted or -EPERM if the packet should be tossed.
+  *
+  */
+-int sk_filter(struct sock *sk, struct sk_buff *skb)
++int sk_filter_trim_cap(struct sock *sk, struct sk_buff *skb, unsigned int cap)
+ {
+ 	int err;
+ 	struct sk_filter *filter;
+@@ -88,14 +89,13 @@ int sk_filter(struct sock *sk, struct sk
+ 	filter = rcu_dereference(sk->sk_filter);
+ 	if (filter) {
+ 		unsigned int pkt_len = SK_RUN_FILTER(filter, skb);
+-
+-		err = pkt_len ? pskb_trim(skb, pkt_len) : -EPERM;
++		err = pkt_len ? pskb_trim(skb, max(cap, pkt_len)) : -EPERM;
+ 	}
+ 	rcu_read_unlock();
+ 
+ 	return err;
+ }
+-EXPORT_SYMBOL(sk_filter);
++EXPORT_SYMBOL(sk_filter_trim_cap);
+ 
+ /**
+  *	sk_run_filter - run a filter on a socket
+--- a/net/rose/rose_in.c
++++ b/net/rose/rose_in.c
+@@ -165,7 +165,8 @@ static int rose_state3_machine(struct so
+ 		rose_frames_acked(sk, nr);
+ 		if (ns == rose->vr) {
+ 			rose_start_idletimer(sk);
+-			if (sock_queue_rcv_skb(sk, skb) == 0) {
++			if (sk_filter_trim_cap(sk, skb, ROSE_MIN_LEN) == 0 &&
++			    __sock_queue_rcv_skb(sk, skb) == 0) {
+ 				rose->vr = (rose->vr + 1) % ROSE_MODULUS;
+ 				queued = 1;
+ 			} else {
diff --git a/debian/patches/bugfix/all/tcp-take-care-of-truncations-done-by-sk_filter.patch b/debian/patches/bugfix/all/tcp-take-care-of-truncations-done-by-sk_filter.patch
new file mode 100644
index 0000000..f55e2a3
--- /dev/null
+++ b/debian/patches/bugfix/all/tcp-take-care-of-truncations-done-by-sk_filter.patch
@@ -0,0 +1,98 @@
+From: Eric Dumazet <edumazet at google.com>
+Date: Thu, 10 Nov 2016 13:12:35 -0800
+Subject: tcp: take care of truncations done by sk_filter()
+Origin: https://git.kernel.org/linus/ac6e780070e30e4c35bd395acfe9191e6268bdd3
+Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2016-8645
+
+With syzkaller help, Marco Grassi found a bug in TCP stack,
+crashing in tcp_collapse()
+
+Root cause is that sk_filter() can truncate the incoming skb,
+but TCP stack was not really expecting this to happen.
+It probably was expecting a simple DROP or ACCEPT behavior.
+
+We first need to make sure no part of TCP header could be removed.
+Then we need to adjust TCP_SKB_CB(skb)->end_seq
+
+Many thanks to syzkaller team and Marco for giving us a reproducer.
+
+Signed-off-by: Eric Dumazet <edumazet at google.com>
+Reported-by: Marco Grassi <marco.gra at gmail.com>
+Reported-by: Vladis Dronov <vdronov at redhat.com>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+[bwh: Backported to 3.2: adjust context]
+---
+ include/net/tcp.h   |  1 +
+ net/ipv4/tcp_ipv4.c | 19 ++++++++++++++++++-
+ net/ipv6/tcp_ipv6.c |  6 ++++--
+ 3 files changed, 23 insertions(+), 3 deletions(-)
+
+--- a/include/net/tcp.h
++++ b/include/net/tcp.h
+@@ -966,6 +966,7 @@ static inline int tcp_prequeue(struct so
+ 	return 1;
+ }
+ 
++int tcp_filter(struct sock *sk, struct sk_buff *skb);
+ 
+ #undef STATE_TRACE
+ 
+--- a/net/ipv4/tcp_ipv4.c
++++ b/net/ipv4/tcp_ipv4.c
+@@ -1647,6 +1647,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
+  */
+@@ -1709,8 +1724,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;
+ 
+--- a/net/ipv6/tcp_ipv6.c
++++ b/net/ipv6/tcp_ipv6.c
+@@ -1585,7 +1585,7 @@ static int tcp_v6_do_rcv(struct sock *sk
+ 		goto discard;
+ #endif
+ 
+-	if (sk_filter(sk, skb))
++	if (tcp_filter(sk, skb))
+ 		goto discard;
+ 
+ 	/*
+@@ -1743,8 +1743,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;
+ 
diff --git a/debian/patches/debian/net-fix-abi-change-for-sk_filter-changes.patch b/debian/patches/debian/net-fix-abi-change-for-sk_filter-changes.patch
new file mode 100644
index 0000000..40d6102
--- /dev/null
+++ b/debian/patches/debian/net-fix-abi-change-for-sk_filter-changes.patch
@@ -0,0 +1,68 @@
+From: Ben Hutchings <ben at decadent.org.uk>
+Date: Thu, 29 Dec 2016 01:31:15 +0000
+Subject: net: Fix ABI change for sk_filter changes
+Forwarded: not-needed
+
+Restore sk_filter() and sk_receive_skb() as exported functions rather
+than inlines.
+
+---
+--- a/include/linux/filter.h
++++ b/include/linux/filter.h
+@@ -151,10 +151,7 @@ static inline unsigned int sk_filter_len
+ }
+ 
+ int sk_filter_trim_cap(struct sock *sk, struct sk_buff *skb, unsigned int cap);
+-static inline int sk_filter(struct sock *sk, struct sk_buff *skb)
+-{
+-	return sk_filter_trim_cap(sk, skb, 1);
+-}
++int sk_filter(struct sock *sk, struct sk_buff *skb);
+ extern unsigned int sk_run_filter(const struct sk_buff *skb,
+ 				  const struct sock_filter *filter);
+ extern int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk);
+--- a/include/net/sock.h
++++ b/include/net/sock.h
+@@ -1271,11 +1271,7 @@ static inline void sock_put(struct sock
+ 
+ int __sk_receive_skb(struct sock *sk, struct sk_buff *skb, const int nested,
+ 		     unsigned int trim_cap);
+-static inline int sk_receive_skb(struct sock *sk, struct sk_buff *skb,
+-				 const int nested)
+-{
+-	return __sk_receive_skb(sk, skb, nested, 1);
+-}
++int sk_receive_skb(struct sock *sk, struct sk_buff *skb, const int nested);
+ 
+ static inline void sk_tx_queue_set(struct sock *sk, int tx_queue)
+ {
+--- a/net/core/filter.c
++++ b/net/core/filter.c
+@@ -97,6 +97,12 @@ int sk_filter_trim_cap(struct sock *sk,
+ }
+ EXPORT_SYMBOL(sk_filter_trim_cap);
+ 
++int sk_filter(struct sock *sk, struct sk_buff *skb)
++{
++	return sk_filter_trim_cap(sk, skb, 1);
++}
++EXPORT_SYMBOL(sk_filter);
++
+ /**
+  *	sk_run_filter - run a filter on a socket
+  *	@skb: buffer to run the filter on
+--- a/net/core/sock.c
++++ b/net/core/sock.c
+@@ -385,6 +385,12 @@ void sk_reset_txq(struct sock *sk)
+ }
+ EXPORT_SYMBOL(sk_reset_txq);
+ 
++int sk_receive_skb(struct sock *sk, struct sk_buff *skb, const int nested)
++{
++	return __sk_receive_skb(sk, skb, nested, 1);
++}
++EXPORT_SYMBOL(sk_receive_skb);
++
+ struct dst_entry *__sk_dst_check(struct sock *sk, u32 cookie)
+ {
+ 	struct dst_entry *dst = __sk_dst_get(sk);
diff --git a/debian/patches/features/all/net-add-__sock_queue_rcv_skb.patch b/debian/patches/features/all/net-add-__sock_queue_rcv_skb.patch
new file mode 100644
index 0000000..1456f65
--- /dev/null
+++ b/debian/patches/features/all/net-add-__sock_queue_rcv_skb.patch
@@ -0,0 +1,63 @@
+From: Ben Hutchings <ben at decadent.org.uk>
+Date: Thu, 29 Dec 2016 03:06:54 +0000
+Subject: net: Add __sock_queue_rcv_skb()
+Forwarded: not-needed
+
+Extraxcted from commit e6afc8ace6dd5cef5e812f26c72579da8806f5ac
+"udp: remove headers from UDP packets before queueing".
+
+Signed-off-by: Ben Hutchings <ben at decadent.org.uk>
+---
+--- a/include/net/sock.h
++++ b/include/net/sock.h
+@@ -1629,6 +1629,7 @@ extern void sk_reset_timer(struct sock *
+ 
+ extern void sk_stop_timer(struct sock *sk, struct timer_list* timer);
+ 
++int __sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb);
+ extern int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb);
+ 
+ extern int sock_queue_err_skb(struct sock *sk, struct sk_buff *skb);
+--- a/net/core/sock.c
++++ b/net/core/sock.c
+@@ -281,9 +281,8 @@ static void sock_disable_timestamp(struc
+ }
+ 
+ 
+-int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
++int __sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
+ {
+-	int err;
+ 	int skb_len;
+ 	unsigned long flags;
+ 	struct sk_buff_head *list = &sk->sk_receive_queue;
+@@ -294,10 +293,6 @@ int sock_queue_rcv_skb(struct sock *sk,
+ 		return -ENOMEM;
+ 	}
+ 
+-	err = sk_filter(sk, skb);
+-	if (err)
+-		return err;
+-
+ 	if (!sk_rmem_schedule(sk, skb->truesize)) {
+ 		atomic_inc(&sk->sk_drops);
+ 		return -ENOBUFS;
+@@ -327,6 +322,18 @@ int sock_queue_rcv_skb(struct sock *sk,
+ 		sk->sk_data_ready(sk, skb_len);
+ 	return 0;
+ }
++EXPORT_SYMBOL(__sock_queue_rcv_skb);
++
++int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
++{
++	int err;
++
++	err = sk_filter(sk, skb);
++	if (err)
++		return err;
++
++	return __sock_queue_rcv_skb(sk, skb);
++}
+ EXPORT_SYMBOL(sock_queue_rcv_skb);
+ 
+ int sk_receive_skb(struct sock *sk, struct sk_buff *skb, const int nested)
diff --git a/debian/patches/series b/debian/patches/series
index 96acca8..362fa3b 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -1120,6 +1120,10 @@ bugfix/all/sg_write-bsg_write-is-not-fit-to-be-called-under-ker.patch
 bugfix/x86/fix-potential-infoleak-in-older-kernels.patch
 bugfix/x86/kvm-x86-drop-error-recovery-in-em_jmp_far-and-em_ret.patch
 bugfix/all/alsa-pcm-call-kill_fasync-in-stream-lock.patch
+features/all/net-add-__sock_queue_rcv_skb.patch
+bugfix/all/rose-limit-sk_filter-trim-to-payload.patch
+bugfix/all/dccp-limit-sk_filter-trim-to-payload.patch
+bugfix/all/tcp-take-care-of-truncations-done-by-sk_filter.patch
 
 # ABI maintenance
 debian/perf-hide-abi-change-in-3.2.30.patch
@@ -1187,3 +1191,4 @@ debian/fs-fix-abi-change-for-aufs-f_setfl-fix.patch
 debian/fs-move-procfs-ecryptfs-stacking-check-into-ecryptfs.patch
 debian/i8042-revert-abi-break-in-3.2.84.patch
 debian/fs-fix-abi-change-in-3.2.84.patch
+debian/net-fix-abi-change-for-sk_filter-changes.patch

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/kernel/linux.git



More information about the Kernel-svn-changes mailing list