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

Commit a0a69a01 authored by Herbert Xu's avatar Herbert Xu Committed by David S. Miller
Browse files

gro: Fix use after free in tcp_gro_receive



After calling skb_gro_receive skb->len can no longer be relied
on since if the skb was merged using frags, then its pages will
have been removed and the length reduced.

This caused tcp_gro_receive to prematurely end merging which
resulted in suboptimal performance with ixgbe.

The fix is to store skb->len on the stack.

Reported-by: default avatarMark Wagner <mwagner@redhat.com>
Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 9dd014eb
Loading
Loading
Loading
Loading
+4 −2
Original line number Original line Diff line number Diff line
@@ -2511,6 +2511,7 @@ struct sk_buff **tcp_gro_receive(struct sk_buff **head, struct sk_buff *skb)
	struct sk_buff *p;
	struct sk_buff *p;
	struct tcphdr *th;
	struct tcphdr *th;
	struct tcphdr *th2;
	struct tcphdr *th2;
	unsigned int len;
	unsigned int thlen;
	unsigned int thlen;
	unsigned int flags;
	unsigned int flags;
	unsigned int mss = 1;
	unsigned int mss = 1;
@@ -2531,6 +2532,7 @@ struct sk_buff **tcp_gro_receive(struct sk_buff **head, struct sk_buff *skb)


	skb_gro_pull(skb, thlen);
	skb_gro_pull(skb, thlen);


	len = skb_gro_len(skb);
	flags = tcp_flag_word(th);
	flags = tcp_flag_word(th);


	for (; (p = *head); head = &p->next) {
	for (; (p = *head); head = &p->next) {
@@ -2561,7 +2563,7 @@ struct sk_buff **tcp_gro_receive(struct sk_buff **head, struct sk_buff *skb)


	mss = skb_shinfo(p)->gso_size;
	mss = skb_shinfo(p)->gso_size;


	flush |= (skb_gro_len(skb) > mss) | !skb_gro_len(skb);
	flush |= (len > mss) | !len;
	flush |= (ntohl(th2->seq) + skb_gro_len(p)) ^ ntohl(th->seq);
	flush |= (ntohl(th2->seq) + skb_gro_len(p)) ^ ntohl(th->seq);


	if (flush || skb_gro_receive(head, skb)) {
	if (flush || skb_gro_receive(head, skb)) {
@@ -2574,7 +2576,7 @@ struct sk_buff **tcp_gro_receive(struct sk_buff **head, struct sk_buff *skb)
	tcp_flag_word(th2) |= flags & (TCP_FLAG_FIN | TCP_FLAG_PSH);
	tcp_flag_word(th2) |= flags & (TCP_FLAG_FIN | TCP_FLAG_PSH);


out_check_final:
out_check_final:
	flush = skb_gro_len(skb) < mss;
	flush = len < mss;
	flush |= flags & (TCP_FLAG_URG | TCP_FLAG_PSH | TCP_FLAG_RST |
	flush |= flags & (TCP_FLAG_URG | TCP_FLAG_PSH | TCP_FLAG_RST |
			  TCP_FLAG_SYN | TCP_FLAG_FIN);
			  TCP_FLAG_SYN | TCP_FLAG_FIN);