diff options
author | Eric Dumazet <edumazet@google.com> | 2013-10-15 14:54:30 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2013-10-17 16:08:08 -0400 |
commit | c52e2421f7368fd36cbe330d2cf41b10452e39a9 (patch) | |
tree | 337ead817a96c313068b3d4b0c4f844ef23014f9 /net/ipv4 | |
parent | df709a18cae475de38ddaff63584692d50267b92 (diff) |
tcp: must unclone packets before mangling them
TCP stack should make sure it owns skbs before mangling them.
We had various crashes using bnx2x, and it turned out gso_size
was cleared right before bnx2x driver was populating TC descriptor
of the _previous_ packet send. TCP stack can sometime retransmit
packets that are still in Qdisc.
Of course we could make bnx2x driver more robust (using
ACCESS_ONCE(shinfo->gso_size) for example), but the bug is TCP stack.
We have identified two points where skb_unclone() was needed.
This patch adds a WARN_ON_ONCE() to warn us if we missed another
fix of this kind.
Kudos to Neal for finding the root cause of this bug. Its visible
using small MSS.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: Neal Cardwell <ncardwell@google.com>
Cc: Yuchung Cheng <ycheng@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4')
-rw-r--r-- | net/ipv4/tcp_output.c | 9 |
1 files changed, 6 insertions, 3 deletions
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index c6f01f2cdb32..8fad1c155688 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c | |||
@@ -986,6 +986,9 @@ static void tcp_queue_skb(struct sock *sk, struct sk_buff *skb) | |||
986 | static void tcp_set_skb_tso_segs(const struct sock *sk, struct sk_buff *skb, | 986 | static void tcp_set_skb_tso_segs(const struct sock *sk, struct sk_buff *skb, |
987 | unsigned int mss_now) | 987 | unsigned int mss_now) |
988 | { | 988 | { |
989 | /* Make sure we own this skb before messing gso_size/gso_segs */ | ||
990 | WARN_ON_ONCE(skb_cloned(skb)); | ||
991 | |||
989 | if (skb->len <= mss_now || !sk_can_gso(sk) || | 992 | if (skb->len <= mss_now || !sk_can_gso(sk) || |
990 | skb->ip_summed == CHECKSUM_NONE) { | 993 | skb->ip_summed == CHECKSUM_NONE) { |
991 | /* Avoid the costly divide in the normal | 994 | /* Avoid the costly divide in the normal |
@@ -1067,9 +1070,7 @@ int tcp_fragment(struct sock *sk, struct sk_buff *skb, u32 len, | |||
1067 | if (nsize < 0) | 1070 | if (nsize < 0) |
1068 | nsize = 0; | 1071 | nsize = 0; |
1069 | 1072 | ||
1070 | if (skb_cloned(skb) && | 1073 | if (skb_unclone(skb, GFP_ATOMIC)) |
1071 | skb_is_nonlinear(skb) && | ||
1072 | pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) | ||
1073 | return -ENOMEM; | 1074 | return -ENOMEM; |
1074 | 1075 | ||
1075 | /* Get a new skb... force flag on. */ | 1076 | /* Get a new skb... force flag on. */ |
@@ -2344,6 +2345,8 @@ int __tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb) | |||
2344 | int oldpcount = tcp_skb_pcount(skb); | 2345 | int oldpcount = tcp_skb_pcount(skb); |
2345 | 2346 | ||
2346 | if (unlikely(oldpcount > 1)) { | 2347 | if (unlikely(oldpcount > 1)) { |
2348 | if (skb_unclone(skb, GFP_ATOMIC)) | ||
2349 | return -ENOMEM; | ||
2347 | tcp_init_tso_segs(sk, skb, cur_mss); | 2350 | tcp_init_tso_segs(sk, skb, cur_mss); |
2348 | tcp_adjust_pcount(sk, skb, oldpcount - tcp_skb_pcount(skb)); | 2351 | tcp_adjust_pcount(sk, skb, oldpcount - tcp_skb_pcount(skb)); |
2349 | } | 2352 | } |