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

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
...@@ -2511,6 +2511,7 @@ struct sk_buff **tcp_gro_receive(struct sk_buff **head, struct sk_buff *skb) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment