aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Dumazet <edumazet@google.com>2017-10-31 02:08:20 -0400
committerDavid S. Miller <davem@davemloft.net>2017-11-01 08:18:34 -0400
commit2b7cda9c35d3b940eb9ce74b30bbd5eb30db493d (patch)
tree5e6be10ad3727cadc7da624a4c10b7660f5bf74c
parente669b86945478b3d90d2d87e3793a6eed06d332f (diff)
tcp: fix tcp_mtu_probe() vs highest_sack
Based on SNMP values provided by Roman, Yuchung made the observation that some crashes in tcp_sacktag_walk() might be caused by MTU probing. Looking at tcp_mtu_probe(), I found that when a new skb was placed in front of the write queue, we were not updating tcp highest sack. If one skb is freed because all its content was copied to the new skb (for MTU probing), then tp->highest_sack could point to a now freed skb. Bad things would then happen, including infinite loops. This patch renames tcp_highest_sack_combine() and uses it from tcp_mtu_probe() to fix the bug. Note that I also removed one test against tp->sacked_out, since we want to replace tp->highest_sack regardless of whatever condition, since keeping a stale pointer to freed skb is a recipe for disaster. Fixes: a47e5a988a57 ("[TCP]: Convert highest_sack to sk_buff to allow direct access") Signed-off-by: Eric Dumazet <edumazet@google.com> Reported-by: Alexei Starovoitov <alexei.starovoitov@gmail.com> Reported-by: Roman Gushchin <guro@fb.com> Reported-by: Oleksandr Natalenko <oleksandr@natalenko.name> Acked-by: Alexei Starovoitov <ast@kernel.org> Acked-by: Neal Cardwell <ncardwell@google.com> Acked-by: Yuchung Cheng <ycheng@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/net/tcp.h6
-rw-r--r--net/ipv4/tcp_output.c3
2 files changed, 5 insertions, 4 deletions
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 33599d17522d..e6d0002a1b0b 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -1771,12 +1771,12 @@ static inline void tcp_highest_sack_reset(struct sock *sk)
1771 tcp_sk(sk)->highest_sack = tcp_write_queue_head(sk); 1771 tcp_sk(sk)->highest_sack = tcp_write_queue_head(sk);
1772} 1772}
1773 1773
1774/* Called when old skb is about to be deleted (to be combined with new skb) */ 1774/* Called when old skb is about to be deleted and replaced by new skb */
1775static inline void tcp_highest_sack_combine(struct sock *sk, 1775static inline void tcp_highest_sack_replace(struct sock *sk,
1776 struct sk_buff *old, 1776 struct sk_buff *old,
1777 struct sk_buff *new) 1777 struct sk_buff *new)
1778{ 1778{
1779 if (tcp_sk(sk)->sacked_out && (old == tcp_sk(sk)->highest_sack)) 1779 if (old == tcp_highest_sack(sk))
1780 tcp_sk(sk)->highest_sack = new; 1780 tcp_sk(sk)->highest_sack = new;
1781} 1781}
1782 1782
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index ae60dd3faed0..823003eef3a2 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -2062,6 +2062,7 @@ static int tcp_mtu_probe(struct sock *sk)
2062 nskb->ip_summed = skb->ip_summed; 2062 nskb->ip_summed = skb->ip_summed;
2063 2063
2064 tcp_insert_write_queue_before(nskb, skb, sk); 2064 tcp_insert_write_queue_before(nskb, skb, sk);
2065 tcp_highest_sack_replace(sk, skb, nskb);
2065 2066
2066 len = 0; 2067 len = 0;
2067 tcp_for_write_queue_from_safe(skb, next, sk) { 2068 tcp_for_write_queue_from_safe(skb, next, sk) {
@@ -2665,7 +2666,7 @@ static bool tcp_collapse_retrans(struct sock *sk, struct sk_buff *skb)
2665 else if (!skb_shift(skb, next_skb, next_skb_size)) 2666 else if (!skb_shift(skb, next_skb, next_skb_size))
2666 return false; 2667 return false;
2667 } 2668 }
2668 tcp_highest_sack_combine(sk, next_skb, skb); 2669 tcp_highest_sack_replace(sk, next_skb, skb);
2669 2670
2670 tcp_unlink_write_queue(next_skb, sk); 2671 tcp_unlink_write_queue(next_skb, sk);
2671 2672