Commit ca2c1418 authored by Paolo Abeni's avatar Paolo Abeni Committed by David S. Miller

udp: drop head states only when all skb references are gone

After commit 0ddf3fb2 ("udp: preserve skb->dst if required
for IP options processing") we clear the skb head state as soon
as the skb carrying them is first processed.

Since the same skb can be processed several times when MSG_PEEK
is used, we can end up lacking the required head states, and
eventually oopsing.

Fix this clearing the skb head state only when processing the
last skb reference.
Reported-by: default avatarEric Dumazet <edumazet@google.com>
Fixes: 0ddf3fb2 ("udp: preserve skb->dst if required for IP options processing")
Signed-off-by: default avatarPaolo Abeni <pabeni@redhat.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 5c25f30c
...@@ -958,7 +958,7 @@ void kfree_skb(struct sk_buff *skb); ...@@ -958,7 +958,7 @@ void kfree_skb(struct sk_buff *skb);
void kfree_skb_list(struct sk_buff *segs); void kfree_skb_list(struct sk_buff *segs);
void skb_tx_error(struct sk_buff *skb); void skb_tx_error(struct sk_buff *skb);
void consume_skb(struct sk_buff *skb); void consume_skb(struct sk_buff *skb);
void consume_stateless_skb(struct sk_buff *skb); void __consume_stateless_skb(struct sk_buff *skb);
void __kfree_skb(struct sk_buff *skb); void __kfree_skb(struct sk_buff *skb);
extern struct kmem_cache *skbuff_head_cache; extern struct kmem_cache *skbuff_head_cache;
......
...@@ -710,14 +710,11 @@ EXPORT_SYMBOL(consume_skb); ...@@ -710,14 +710,11 @@ EXPORT_SYMBOL(consume_skb);
* consume_stateless_skb - free an skbuff, assuming it is stateless * consume_stateless_skb - free an skbuff, assuming it is stateless
* @skb: buffer to free * @skb: buffer to free
* *
* Works like consume_skb(), but this variant assumes that all the head * Alike consume_skb(), but this variant assumes that this is the last
* states have been already dropped. * skb reference and all the head states have been already dropped
*/ */
void consume_stateless_skb(struct sk_buff *skb) void __consume_stateless_skb(struct sk_buff *skb)
{ {
if (!skb_unref(skb))
return;
trace_consume_skb(skb); trace_consume_skb(skb);
skb_release_data(skb); skb_release_data(skb);
kfree_skbmem(skb); kfree_skbmem(skb);
......
...@@ -1397,12 +1397,15 @@ void skb_consume_udp(struct sock *sk, struct sk_buff *skb, int len) ...@@ -1397,12 +1397,15 @@ void skb_consume_udp(struct sock *sk, struct sk_buff *skb, int len)
unlock_sock_fast(sk, slow); unlock_sock_fast(sk, slow);
} }
if (!skb_unref(skb))
return;
/* In the more common cases we cleared the head states previously, /* In the more common cases we cleared the head states previously,
* see __udp_queue_rcv_skb(). * see __udp_queue_rcv_skb().
*/ */
if (unlikely(udp_skb_has_head_state(skb))) if (unlikely(udp_skb_has_head_state(skb)))
skb_release_head_state(skb); skb_release_head_state(skb);
consume_stateless_skb(skb); __consume_stateless_skb(skb);
} }
EXPORT_SYMBOL_GPL(skb_consume_udp); EXPORT_SYMBOL_GPL(skb_consume_udp);
......
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