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: Mark Wagner <mwagner@redhat.com> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Родитель
9dd014eb98
Коммит
a0a69a0106
|
@ -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 @@ found:
|
||||||
|
|
||||||
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 @@ found:
|
||||||
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);
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче