Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 25c2755f authored by Sabrina Dubroca's avatar Sabrina Dubroca Committed by Akshaya
Browse files

net: add recursion limit to GRO

[ Backported upstream commit: fcd91dd449867c6bfe56a81cabba76b829fd05cd]
[ Files without GRO support have not been updated ]

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.  This recursion
counter is put in the GRO CB, but could be turned into a percpu counter
if we run out of space in the CB.

Thanks to Vladimír Beneš <vbenes@redhat.com> for the initial bug report.
Fixes: CVE-2016-7039

Change-Id: Ibf6b3fb2707ae114b2f960e672b2a7d4ecf6bcf0
Git-repo: http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git


Signed-off-by: default avatarSabrina Dubroca <sd@queasysnail.net>
Reviewed-by: default avatarJiri Benc <jbenc@redhat.com>
Acked-by: default avatarHannes Frederic Sowa <hannes@stressinduktion.org>
Acked-by: default avatarTom Herbert <tom@herbertland.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
Signed-off-by: default avatarAkshaya <akshayab@codeaurora.org>
parent 1a7276f0
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -600,7 +600,7 @@ static struct sk_buff **vxlan_gro_receive(struct sk_buff **head, struct sk_buff

	skb_gro_pull(skb, sizeof(*eh)); /* pull inner eth header */
	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();
+24 −0
Original line number Diff line number Diff line
@@ -1916,6 +1916,11 @@ struct napi_gro_cb {

	/* 7 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;

@@ -1925,6 +1930,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 (unlikely(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	     */
+1 −0
Original line number Diff line number Diff line
@@ -4118,6 +4118,7 @@ static enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff
		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;

		/* Setup for GRO checksum validation */
		switch (skb->ip_summed) {
+1 −1
Original line number Diff line number Diff line
@@ -1409,7 +1409,7 @@ static struct sk_buff **inet_gro_receive(struct sk_buff **head,
	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();
+2 −2
Original line number Diff line number Diff line
@@ -127,7 +127,7 @@ static struct sk_buff **fou_gro_receive(struct sk_buff **head,
	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();
@@ -236,7 +236,7 @@ static struct sk_buff **gue_gro_receive(struct sk_buff **head,
	/* Adjusted NAPI_GRO_CB(skb)->csum after skb_gro_pull()*/
	skb_gro_postpull_rcsum(skb, guehdr, guehlen);

	pp = ops->callbacks.gro_receive(head, skb);
	pp = call_gro_receive(ops->callbacks.gro_receive, head, skb);

out_unlock:
	rcu_read_unlock();
Loading