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

[IPV4/IPV6] Ensure all frag_list members have NULL sk

Having frag_list members which holds wmem of an sk leads to nightmares
with partially cloned frag skb's.  The reason is that once you unleash
a skb with a frag_list that has individual sk ownerships into the stack
you can never undo those ownerships safely as they may have been cloned
by things like netfilter.  Since we have to undo them in order to make
skb_linearize happy this approach leads to a dead-end.

So let's go the other way and make this an invariant:

	For any skb on a frag_list, skb->sk must be NULL.

That is, the socket ownership always belongs to the head skb.
It turns out that the implementation is actually pretty simple.

The above invariant is actually violated in the following patch
for a short duration inside ip_fragment.  This is OK because the
offending frag_list member is either destroyed at the end of the
slow path without being sent anywhere, or it is detached from
the frag_list before being sent.
Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent d4810200
...@@ -490,6 +490,14 @@ int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*)) ...@@ -490,6 +490,14 @@ int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*))
/* Partially cloned skb? */ /* Partially cloned skb? */
if (skb_shared(frag)) if (skb_shared(frag))
goto slow_path; goto slow_path;
BUG_ON(frag->sk);
if (skb->sk) {
sock_hold(skb->sk);
frag->sk = skb->sk;
frag->destructor = sock_wfree;
skb->truesize -= frag->truesize;
}
} }
/* Everything is OK. Generate! */ /* Everything is OK. Generate! */
......
...@@ -552,13 +552,17 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) ...@@ -552,13 +552,17 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
skb_headroom(frag) < hlen) skb_headroom(frag) < hlen)
goto slow_path; goto slow_path;
/* Correct socket ownership. */
if (frag->sk == NULL)
goto slow_path;
/* Partially cloned skb? */ /* Partially cloned skb? */
if (skb_shared(frag)) if (skb_shared(frag))
goto slow_path; goto slow_path;
BUG_ON(frag->sk);
if (skb->sk) {
sock_hold(skb->sk);
frag->sk = skb->sk;
frag->destructor = sock_wfree;
skb->truesize -= frag->truesize;
}
} }
err = 0; err = 0;
...@@ -1116,12 +1120,10 @@ int ip6_push_pending_frames(struct sock *sk) ...@@ -1116,12 +1120,10 @@ int ip6_push_pending_frames(struct sock *sk)
tail_skb = &(tmp_skb->next); tail_skb = &(tmp_skb->next);
skb->len += tmp_skb->len; skb->len += tmp_skb->len;
skb->data_len += tmp_skb->len; skb->data_len += tmp_skb->len;
#if 0 /* Logically correct, but useless work, ip_fragment() will have to undo */
skb->truesize += tmp_skb->truesize; skb->truesize += tmp_skb->truesize;
__sock_put(tmp_skb->sk); __sock_put(tmp_skb->sk);
tmp_skb->destructor = NULL; tmp_skb->destructor = NULL;
tmp_skb->sk = NULL; tmp_skb->sk = NULL;
#endif
} }
ipv6_addr_copy(final_dst, &fl->fl6_dst); ipv6_addr_copy(final_dst, &fl->fl6_dst);
......
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