| From foo@baz Thu Sep 14 23:20:08 PDT 2017 |
| From: Eric Dumazet <edumazet@google.com> |
| Date: Tue, 22 Aug 2017 09:39:28 -0700 |
| Subject: udp: on peeking bad csum, drop packets even if not at head |
| |
| From: Eric Dumazet <edumazet@google.com> |
| |
| |
| [ Upstream commit fd6055a806edc4019be1b9fb7d25262599bca5b1 ] |
| |
| When peeking, if a bad csum is discovered, the skb is unlinked from |
| the queue with __sk_queue_drop_skb and the peek operation restarted. |
| |
| __sk_queue_drop_skb only drops packets that match the queue head. |
| |
| This fails if the skb was found after the head, using SO_PEEK_OFF |
| socket option. This causes an infinite loop. |
| |
| We MUST drop this problematic skb, and we can simply check if skb was |
| already removed by another thread, by looking at skb->next : |
| |
| This pointer is set to NULL by the __skb_unlink() operation, that might |
| have happened only under the spinlock protection. |
| |
| Many thanks to syzkaller team (and particularly Dmitry Vyukov who |
| provided us nice C reproducers exhibiting the lockup) and Willem de |
| Bruijn who provided first version for this patch and a test program. |
| |
| Fixes: 627d2d6b5500 ("udp: enable MSG_PEEK at non-zero offset") |
| Signed-off-by: Eric Dumazet <edumazet@google.com> |
| Reported-by: Dmitry Vyukov <dvyukov@google.com> |
| Cc: Willem de Bruijn <willemb@google.com> |
| Acked-by: Paolo Abeni <pabeni@redhat.com> |
| Acked-by: Willem de Bruijn <willemb@google.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| net/core/datagram.c | 2 +- |
| 1 file changed, 1 insertion(+), 1 deletion(-) |
| |
| --- a/net/core/datagram.c |
| +++ b/net/core/datagram.c |
| @@ -351,7 +351,7 @@ int skb_kill_datagram(struct sock *sk, s |
| if (flags & MSG_PEEK) { |
| err = -ENOENT; |
| spin_lock_bh(&sk->sk_receive_queue.lock); |
| - if (skb == skb_peek(&sk->sk_receive_queue)) { |
| + if (skb->next) { |
| __skb_unlink(skb, &sk->sk_receive_queue); |
| atomic_dec(&skb->users); |
| err = 0; |