net: add recursion limit to GRO (CVE-2016-7039)
This commit is contained in:
parent
2456c48897
commit
c27b72f255
|
@ -124,6 +124,9 @@ linux (4.7.7-1) UNRELEASED; urgency=medium
|
|||
laptops
|
||||
- [x86] ALSA: hda - Add the top speaker pin config for HP Spectre x360
|
||||
|
||||
[ Ben Hutchings ]
|
||||
* net: add recursion limit to GRO (CVE-2016-7039)
|
||||
|
||||
-- Ben Hutchings <ben@decadent.org.uk> Tue, 11 Oct 2016 22:43:14 +0100
|
||||
|
||||
linux (4.7.6-1) unstable; urgency=medium
|
||||
|
|
|
@ -0,0 +1,201 @@
|
|||
From: Sabrina Dubroca <sd@queasysnail.net>
|
||||
Date: Mon, 10 Oct 2016 15:43:46 +0200
|
||||
Subject: net: add recursion limit to GRO
|
||||
Origin: https://patchwork.ozlabs.org/patch/680412/
|
||||
|
||||
Currently, GRO can do unlimited recursion through the gro_receive
|
||||
handlers. This was fixed for tunneling protocols by limiting tunnel GRO
|
||||
to one level with encap_mark, but both VLAN and TEB still have this
|
||||
problem. Thus, the kernel is vulnerable to a stack overflow, if we
|
||||
receive a packet composed entirely of VLAN headers.
|
||||
|
||||
This patch adds a recursion counter to the GRO layer to prevent stack
|
||||
overflow. When a gro_receive function hits the recursion limit, GRO is
|
||||
aborted for this skb and it is processed normally.
|
||||
|
||||
Thanks to Vladimír Beneš <vbenes@redhat.com> for the initial bug report.
|
||||
|
||||
Fixes: CVE-2016-7039
|
||||
Fixes: 9b174d88c257 ("net: Add Transparent Ethernet Bridging GRO support.")
|
||||
Fixes: 66e5133f19e9 ("vlan: Add GRO support for non hardware accelerated vlan")
|
||||
Signed-off-by: Sabrina Dubroca <sd@queasysnail.net>
|
||||
Reviewed-by: Jiri Benc <jbenc@redhat.com>
|
||||
Acked-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
|
||||
---
|
||||
drivers/net/geneve.c | 2 +-
|
||||
drivers/net/vxlan.c | 2 +-
|
||||
include/linux/netdevice.h | 24 +++++++++++++++++++++++-
|
||||
net/8021q/vlan.c | 2 +-
|
||||
net/core/dev.c | 1 +
|
||||
net/ethernet/eth.c | 2 +-
|
||||
net/ipv4/af_inet.c | 2 +-
|
||||
net/ipv4/fou.c | 4 ++--
|
||||
net/ipv4/gre_offload.c | 2 +-
|
||||
net/ipv4/udp_offload.c | 8 +++++++-
|
||||
net/ipv6/ip6_offload.c | 2 +-
|
||||
11 files changed, 40 insertions(+), 11 deletions(-)
|
||||
|
||||
--- a/drivers/net/geneve.c
|
||||
+++ b/drivers/net/geneve.c
|
||||
@@ -471,7 +471,7 @@ static struct sk_buff **geneve_gro_recei
|
||||
|
||||
skb_gro_pull(skb, gh_len);
|
||||
skb_gro_postpull_rcsum(skb, gh, gh_len);
|
||||
- pp = ptype->callbacks.gro_receive(head, skb);
|
||||
+ pp = call_gro_receive(ptype->callbacks.gro_receive, head, skb);
|
||||
flush = 0;
|
||||
|
||||
out_unlock:
|
||||
--- a/drivers/net/vxlan.c
|
||||
+++ b/drivers/net/vxlan.c
|
||||
@@ -601,7 +601,7 @@ static struct sk_buff **vxlan_gro_receiv
|
||||
}
|
||||
}
|
||||
|
||||
- pp = eth_gro_receive(head, skb);
|
||||
+ pp = call_gro_receive(eth_gro_receive, head, skb);
|
||||
flush = 0;
|
||||
|
||||
out:
|
||||
--- a/include/linux/netdevice.h
|
||||
+++ b/include/linux/netdevice.h
|
||||
@@ -2114,7 +2114,10 @@ struct napi_gro_cb {
|
||||
/* Used to determine if flush_id can be ignored */
|
||||
u8 is_atomic:1;
|
||||
|
||||
- /* 5 bit hole */
|
||||
+ /* Number of gro_receive callbacks this packet already went through */
|
||||
+ u8 recursion_counter:4;
|
||||
+
|
||||
+ /* 1 bit hole */
|
||||
|
||||
/* used to support CHECKSUM_COMPLETE for tunneling protocols */
|
||||
__wsum csum;
|
||||
@@ -2125,6 +2128,25 @@ struct napi_gro_cb {
|
||||
|
||||
#define NAPI_GRO_CB(skb) ((struct napi_gro_cb *)(skb)->cb)
|
||||
|
||||
+#define GRO_RECURSION_LIMIT 15
|
||||
+static inline int gro_recursion_inc_test(struct sk_buff *skb)
|
||||
+{
|
||||
+ return ++NAPI_GRO_CB(skb)->recursion_counter == GRO_RECURSION_LIMIT;
|
||||
+}
|
||||
+
|
||||
+typedef struct sk_buff **(*gro_receive_t)(struct sk_buff **, struct sk_buff *);
|
||||
+static inline struct sk_buff **call_gro_receive(gro_receive_t cb,
|
||||
+ struct sk_buff **head,
|
||||
+ struct sk_buff *skb)
|
||||
+{
|
||||
+ if (gro_recursion_inc_test(skb)) {
|
||||
+ NAPI_GRO_CB(skb)->flush |= 1;
|
||||
+ return NULL;
|
||||
+ }
|
||||
+
|
||||
+ return cb(head, skb);
|
||||
+}
|
||||
+
|
||||
struct packet_type {
|
||||
__be16 type; /* This is really htons(ether_type). */
|
||||
struct net_device *dev; /* NULL is wildcarded here */
|
||||
--- a/net/8021q/vlan.c
|
||||
+++ b/net/8021q/vlan.c
|
||||
@@ -664,7 +664,7 @@ static struct sk_buff **vlan_gro_receive
|
||||
|
||||
skb_gro_pull(skb, sizeof(*vhdr));
|
||||
skb_gro_postpull_rcsum(skb, vhdr, sizeof(*vhdr));
|
||||
- pp = ptype->callbacks.gro_receive(head, skb);
|
||||
+ pp = call_gro_receive(ptype->callbacks.gro_receive, head, skb);
|
||||
|
||||
out_unlock:
|
||||
rcu_read_unlock();
|
||||
--- a/net/core/dev.c
|
||||
+++ b/net/core/dev.c
|
||||
@@ -4500,6 +4500,7 @@ static enum gro_result dev_gro_receive(s
|
||||
NAPI_GRO_CB(skb)->flush = 0;
|
||||
NAPI_GRO_CB(skb)->free = 0;
|
||||
NAPI_GRO_CB(skb)->encap_mark = 0;
|
||||
+ NAPI_GRO_CB(skb)->recursion_counter = 0;
|
||||
NAPI_GRO_CB(skb)->is_fou = 0;
|
||||
NAPI_GRO_CB(skb)->is_atomic = 1;
|
||||
NAPI_GRO_CB(skb)->gro_remcsum_start = 0;
|
||||
--- a/net/ethernet/eth.c
|
||||
+++ b/net/ethernet/eth.c
|
||||
@@ -439,7 +439,7 @@ struct sk_buff **eth_gro_receive(struct
|
||||
|
||||
skb_gro_pull(skb, sizeof(*eh));
|
||||
skb_gro_postpull_rcsum(skb, eh, sizeof(*eh));
|
||||
- pp = ptype->callbacks.gro_receive(head, skb);
|
||||
+ pp = call_gro_receive(ptype->callbacks.gro_receive, head, skb);
|
||||
|
||||
out_unlock:
|
||||
rcu_read_unlock();
|
||||
--- a/net/ipv4/af_inet.c
|
||||
+++ b/net/ipv4/af_inet.c
|
||||
@@ -1388,7 +1388,7 @@ struct sk_buff **inet_gro_receive(struct
|
||||
skb_gro_pull(skb, sizeof(*iph));
|
||||
skb_set_transport_header(skb, skb_gro_offset(skb));
|
||||
|
||||
- pp = ops->callbacks.gro_receive(head, skb);
|
||||
+ pp = call_gro_receive(ops->callbacks.gro_receive, head, skb);
|
||||
|
||||
out_unlock:
|
||||
rcu_read_unlock();
|
||||
--- a/net/ipv4/fou.c
|
||||
+++ b/net/ipv4/fou.c
|
||||
@@ -219,7 +219,7 @@ static struct sk_buff **fou_gro_receive(
|
||||
if (!ops || !ops->callbacks.gro_receive)
|
||||
goto out_unlock;
|
||||
|
||||
- pp = ops->callbacks.gro_receive(head, skb);
|
||||
+ pp = call_gro_receive(ops->callbacks.gro_receive, head, skb);
|
||||
|
||||
out_unlock:
|
||||
rcu_read_unlock();
|
||||
@@ -387,7 +387,7 @@ static struct sk_buff **gue_gro_receive(
|
||||
if (WARN_ON_ONCE(!ops || !ops->callbacks.gro_receive))
|
||||
goto out_unlock;
|
||||
|
||||
- pp = ops->callbacks.gro_receive(head, skb);
|
||||
+ pp = call_gro_receive(ops->callbacks.gro_receive, head, skb);
|
||||
flush = 0;
|
||||
|
||||
out_unlock:
|
||||
--- a/net/ipv4/gre_offload.c
|
||||
+++ b/net/ipv4/gre_offload.c
|
||||
@@ -227,7 +227,7 @@ static struct sk_buff **gre_gro_receive(
|
||||
/* Adjusted NAPI_GRO_CB(skb)->csum after skb_gro_pull()*/
|
||||
skb_gro_postpull_rcsum(skb, greh, grehlen);
|
||||
|
||||
- pp = ptype->callbacks.gro_receive(head, skb);
|
||||
+ pp = call_gro_receive(ptype->callbacks.gro_receive, head, skb);
|
||||
flush = 0;
|
||||
|
||||
out_unlock:
|
||||
--- a/net/ipv4/udp_offload.c
|
||||
+++ b/net/ipv4/udp_offload.c
|
||||
@@ -293,7 +293,13 @@ unflush:
|
||||
|
||||
skb_gro_pull(skb, sizeof(struct udphdr)); /* pull encapsulating udp header */
|
||||
skb_gro_postpull_rcsum(skb, uh, sizeof(struct udphdr));
|
||||
- pp = udp_sk(sk)->gro_receive(sk, head, skb);
|
||||
+
|
||||
+ if (gro_recursion_inc_test(skb)) {
|
||||
+ flush = 1;
|
||||
+ pp = NULL;
|
||||
+ } else {
|
||||
+ pp = udp_sk(sk)->gro_receive(sk, head, skb);
|
||||
+ }
|
||||
|
||||
out_unlock:
|
||||
rcu_read_unlock();
|
||||
--- a/net/ipv6/ip6_offload.c
|
||||
+++ b/net/ipv6/ip6_offload.c
|
||||
@@ -243,7 +243,7 @@ static struct sk_buff **ipv6_gro_receive
|
||||
|
||||
skb_gro_postpull_rcsum(skb, iph, nlen);
|
||||
|
||||
- pp = ops->callbacks.gro_receive(head, skb);
|
||||
+ pp = call_gro_receive(ops->callbacks.gro_receive, head, skb);
|
||||
|
||||
out_unlock:
|
||||
rcu_read_unlock();
|
|
@ -0,0 +1,33 @@
|
|||
From: Ben Hutchings <ben@decadent.org.uk>
|
||||
Date: Thu, 13 Oct 2016 00:05:23 +0100
|
||||
Subject: gro: Fix ABI change for CVE-2016-7039 fix
|
||||
Forwarded: not-needed
|
||||
|
||||
The fix for CVE-2016-7039 (unbounded recursion in GRO) added a 4-bit
|
||||
bitfield in struct napi_gro_cb where there used to be padding, so
|
||||
the structure layout hasn't changed.
|
||||
|
||||
In case of a version mismatch between kernel and modules, two things
|
||||
could go wrong, but they are not disastrous:
|
||||
|
||||
- the recursion count is not incremented and checked when it should
|
||||
be, so the fix is incomplete - no worse than before
|
||||
- the recursion count is not reset, so GRO is not effective - only
|
||||
a performance problem
|
||||
|
||||
So hide this change from genksyms.
|
||||
|
||||
---
|
||||
--- a/include/linux/netdevice.h
|
||||
+++ b/include/linux/netdevice.h
|
||||
@@ -2114,8 +2114,10 @@ struct napi_gro_cb {
|
||||
/* Used to determine if flush_id can be ignored */
|
||||
u8 is_atomic:1;
|
||||
|
||||
+#ifndef __GENKSYMS__
|
||||
/* Number of gro_receive callbacks this packet already went through */
|
||||
u8 recursion_counter:4;
|
||||
+#endif
|
||||
|
||||
/* 1 bit hole */
|
||||
|
|
@ -114,12 +114,14 @@ features/all/securelevel/arm64-add-kernel-config-option-to-set-securelevel-wh.pa
|
|||
# Security fixes
|
||||
bugfix/all/ptrace-being-capable-wrt-a-process-requires-mapped-uids-gids.patch
|
||||
debian/i386-686-pae-pci-set-pci-nobios-by-default.patch
|
||||
bugfix/all/net-add-recursion-limit-to-gro.patch
|
||||
|
||||
# ABI maintenance
|
||||
debian/i8042-revert-abi-break-in-4.7.3.patch
|
||||
debian/revert-arm64-define-at_vector_size_arch-for-arch_dlinfo.patch
|
||||
debian/uaccess-avoid-abi-change-in-4.7.5.patch
|
||||
debian/uio-fix-abi-change-in-4.7.5.patch
|
||||
debian/gro-fix-abi-change-for-cve-2016-7039-fix.patch
|
||||
|
||||
# Tools bug fixes
|
||||
bugfix/all/usbip-document-tcp-wrappers.patch
|
||||
|
|
Loading…
Reference in New Issue