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>
This commit is contained in:
parent
df709a18ca
commit
c52e2421f7
|
@ -986,6 +986,9 @@ static void tcp_queue_skb(struct sock *sk, struct sk_buff *skb)
|
||||||
static void tcp_set_skb_tso_segs(const struct sock *sk, struct sk_buff *skb,
|
static void tcp_set_skb_tso_segs(const struct sock *sk, struct sk_buff *skb,
|
||||||
unsigned int mss_now)
|
unsigned int mss_now)
|
||||||
{
|
{
|
||||||
|
/* Make sure we own this skb before messing gso_size/gso_segs */
|
||||||
|
WARN_ON_ONCE(skb_cloned(skb));
|
||||||
|
|
||||||
if (skb->len <= mss_now || !sk_can_gso(sk) ||
|
if (skb->len <= mss_now || !sk_can_gso(sk) ||
|
||||||
skb->ip_summed == CHECKSUM_NONE) {
|
skb->ip_summed == CHECKSUM_NONE) {
|
||||||
/* Avoid the costly divide in the normal
|
/* Avoid the costly divide in the normal
|
||||||
|
@ -1067,9 +1070,7 @@ int tcp_fragment(struct sock *sk, struct sk_buff *skb, u32 len,
|
||||||
if (nsize < 0)
|
if (nsize < 0)
|
||||||
nsize = 0;
|
nsize = 0;
|
||||||
|
|
||||||
if (skb_cloned(skb) &&
|
if (skb_unclone(skb, GFP_ATOMIC))
|
||||||
skb_is_nonlinear(skb) &&
|
|
||||||
pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
|
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
/* Get a new skb... force flag on. */
|
/* Get a new skb... force flag on. */
|
||||||
|
@ -2344,6 +2345,8 @@ int __tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb)
|
||||||
int oldpcount = tcp_skb_pcount(skb);
|
int oldpcount = tcp_skb_pcount(skb);
|
||||||
|
|
||||||
if (unlikely(oldpcount > 1)) {
|
if (unlikely(oldpcount > 1)) {
|
||||||
|
if (skb_unclone(skb, GFP_ATOMIC))
|
||||||
|
return -ENOMEM;
|
||||||
tcp_init_tso_segs(sk, skb, cur_mss);
|
tcp_init_tso_segs(sk, skb, cur_mss);
|
||||||
tcp_adjust_pcount(sk, skb, oldpcount - tcp_skb_pcount(skb));
|
tcp_adjust_pcount(sk, skb, oldpcount - tcp_skb_pcount(skb));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue