* [PATCH net] udp: on peeking bad csum, drop packets even if not at head
@ 2017-08-21 21:39 Willem de Bruijn
2017-08-21 22:37 ` Willem de Bruijn
2017-08-21 22:40 ` Eric Dumazet
0 siblings, 2 replies; 9+ messages in thread
From: Willem de Bruijn @ 2017-08-21 21:39 UTC (permalink / raw)
To: netdev; +Cc: davem, pabeni, Willem de Bruijn
From: Willem de Bruijn <willemb@google.com>
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. With
sk_peek_off, the skb need not be at head, causing the call to fail and
the same skb to be found again on restart.
Walk the queue to find the correct skb. Limit the walk to sk_peek_off,
to bound cycle cost to at most twice the original skb_queue_walk in
__skb_try_recv_from_queue.
The operation may race with updates to sk_peek_off. As the operation
is retried, it will eventually succeed.
Signed-off-by: Willem de Bruijn <willemb@google.com>
---
Simpler would be to check (skb->csum_complete_sw && !sbk->csum_valid)
in __skb_try_recv_from_queue to ignore skbs with bad checksum. But
__udp_lib_checksum_complete does not update those fields if called
while peeking, because the skb is shared. I found no way around that.
---
net/core/datagram.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/net/core/datagram.c b/net/core/datagram.c
index a21ca8dee5ea..5cf32b2372d3 100644
--- a/net/core/datagram.c
+++ b/net/core/datagram.c
@@ -360,9 +360,17 @@ int __sk_queue_drop_skb(struct sock *sk, struct sk_buff_head *sk_queue,
int err = 0;
if (flags & MSG_PEEK) {
+ struct sk_buff *lskb;
+ int off = sk_peek_offset(sk, flags);
+
err = -ENOENT;
spin_lock_bh(&sk_queue->lock);
- if (skb == skb_peek(sk_queue)) {
+ lskb = skb_peek(sk_queue);
+ while (lskb != skb && lskb && off >= lskb->len) {
+ off -= lskb->len;
+ lskb = skb_peek_next(lskb, sk_queue);
+ }
+ if (lskb == skb) {
__skb_unlink(skb, sk_queue);
refcount_dec(&skb->users);
if (destructor)
--
2.14.1.480.gb18f417b89-goog
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH net] udp: on peeking bad csum, drop packets even if not at head
2017-08-21 21:39 [PATCH net] udp: on peeking bad csum, drop packets even if not at head Willem de Bruijn
@ 2017-08-21 22:37 ` Willem de Bruijn
2017-08-21 22:40 ` Eric Dumazet
1 sibling, 0 replies; 9+ messages in thread
From: Willem de Bruijn @ 2017-08-21 22:37 UTC (permalink / raw)
To: Network Development
Cc: David Miller, Paolo Abeni, Willem de Bruijn, Eric Dumazet
On Mon, Aug 21, 2017 at 5:39 PM, Willem de Bruijn
<willemdebruijn.kernel@gmail.com> wrote:
> From: Willem de Bruijn <willemb@google.com>
>
> 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. With
> sk_peek_off, the skb need not be at head, causing the call to fail and
> the same skb to be found again on restart.
>
> Walk the queue to find the correct skb. Limit the walk to sk_peek_off,
> to bound cycle cost to at most twice the original skb_queue_walk in
> __skb_try_recv_from_queue.
>
> The operation may race with updates to sk_peek_off. As the operation
> is retried, it will eventually succeed.
>
> Signed-off-by: Willem de Bruijn <willemb@google.com>
Eric just suggested an alternative that does not require looping, which
is much nicer.
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH net] udp: on peeking bad csum, drop packets even if not at head
2017-08-21 21:39 [PATCH net] udp: on peeking bad csum, drop packets even if not at head Willem de Bruijn
2017-08-21 22:37 ` Willem de Bruijn
@ 2017-08-21 22:40 ` Eric Dumazet
2017-08-22 0:12 ` Willem de Bruijn
2017-08-22 16:39 ` [PATCH v2 " Eric Dumazet
1 sibling, 2 replies; 9+ messages in thread
From: Eric Dumazet @ 2017-08-21 22:40 UTC (permalink / raw)
To: Willem de Bruijn; +Cc: netdev, davem, pabeni, Willem de Bruijn
On Mon, 2017-08-21 at 17:39 -0400, Willem de Bruijn wrote:
> From: Willem de Bruijn <willemb@google.com>
>
> 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. With
> sk_peek_off, the skb need not be at head, causing the call to fail and
> the same skb to be found again on restart.
>
> Walk the queue to find the correct skb. Limit the walk to sk_peek_off,
> to bound cycle cost to at most twice the original skb_queue_walk in
> __skb_try_recv_from_queue.
>
> The operation may race with updates to sk_peek_off. As the operation
> is retried, it will eventually succeed.
>
> Signed-off-by: Willem de Bruijn <willemb@google.com>
You forgot the Fixes: tag, that such a bug fix deserves.
I am not a big fan of your patch and would prefer a solution without the
loop.
skb already have ->next and ->prev pointer telling us its position in
the receive queue.
We only need to make sure we are the last owner of this skb before doing
the check (ie cancel the kfree_skb() that usually follows the call to
v__sk_queue_drop_skb() of skb->next being NULL or not.
Something like :
include/net/sock.h | 2 +-
net/core/datagram.c | 22 ++++++++++++++--------
net/ipv4/udp.c | 2 +-
net/ipv6/udp.c | 2 +-
4 files changed, 17 insertions(+), 11 deletions(-)
diff --git a/include/net/sock.h b/include/net/sock.h
index aeeec62992ca..6e43bab92d95 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -2030,7 +2030,7 @@ void sk_reset_timer(struct sock *sk, struct timer_list *timer,
void sk_stop_timer(struct sock *sk, struct timer_list *timer);
int __sk_queue_drop_skb(struct sock *sk, struct sk_buff_head *sk_queue,
- struct sk_buff *skb, unsigned int flags,
+ struct sk_buff **pskb, unsigned int flags,
void (*destructor)(struct sock *sk,
struct sk_buff *skb));
int __sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb);
diff --git a/net/core/datagram.c b/net/core/datagram.c
index a21ca8dee5ea..7e129f91af89 100644
--- a/net/core/datagram.c
+++ b/net/core/datagram.c
@@ -353,21 +353,27 @@ void __skb_free_datagram_locked(struct sock *sk, struct sk_buff *skb, int len)
EXPORT_SYMBOL(__skb_free_datagram_locked);
int __sk_queue_drop_skb(struct sock *sk, struct sk_buff_head *sk_queue,
- struct sk_buff *skb, unsigned int flags,
+ struct sk_buff **pskb, unsigned int flags,
void (*destructor)(struct sock *sk,
struct sk_buff *skb))
{
int err = 0;
if (flags & MSG_PEEK) {
+ struct sk_buff *skb = *pskb;
+
err = -ENOENT;
spin_lock_bh(&sk_queue->lock);
- if (skb == skb_peek(sk_queue)) {
- __skb_unlink(skb, sk_queue);
- refcount_dec(&skb->users);
- if (destructor)
- destructor(sk, skb);
- err = 0;
+ refcount_dec(&skb->users);
+ *pskb = NULL;
+ if (refcount_dec_if_one(&skb->users)) {
+ if (skb->next) {
+ __skb_unlink(skb, sk_queue);
+ if (destructor)
+ destructor(sk, skb);
+ err = 0;
+ }
+ __kfree_skb(skb);
}
spin_unlock_bh(&sk_queue->lock);
}
@@ -400,7 +406,7 @@ EXPORT_SYMBOL(__sk_queue_drop_skb);
int skb_kill_datagram(struct sock *sk, struct sk_buff *skb, unsigned int flags)
{
- int err = __sk_queue_drop_skb(sk, &sk->sk_receive_queue, skb, flags,
+ int err = __sk_queue_drop_skb(sk, &sk->sk_receive_queue, &skb, flags,
NULL);
kfree_skb(skb);
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index cd1d044a7fa5..b5f90b845a6f 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -1648,7 +1648,7 @@ int udp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int noblock,
return err;
csum_copy_err:
- if (!__sk_queue_drop_skb(sk, &udp_sk(sk)->reader_queue, skb, flags,
+ if (!__sk_queue_drop_skb(sk, &udp_sk(sk)->reader_queue, &skb, flags,
udp_skb_destructor)) {
UDP_INC_STATS(sock_net(sk), UDP_MIB_CSUMERRORS, is_udplite);
UDP_INC_STATS(sock_net(sk), UDP_MIB_INERRORS, is_udplite);
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 20039c8501eb..214a973571fd 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -465,7 +465,7 @@ int udpv6_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
return err;
csum_copy_err:
- if (!__sk_queue_drop_skb(sk, &udp_sk(sk)->reader_queue, skb, flags,
+ if (!__sk_queue_drop_skb(sk, &udp_sk(sk)->reader_queue, &skb, flags,
udp_skb_destructor)) {
if (is_udp4) {
UDP_INC_STATS(sock_net(sk),
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH net] udp: on peeking bad csum, drop packets even if not at head
2017-08-21 22:40 ` Eric Dumazet
@ 2017-08-22 0:12 ` Willem de Bruijn
2017-08-22 1:11 ` Willem de Bruijn
2017-08-22 16:39 ` [PATCH v2 " Eric Dumazet
1 sibling, 1 reply; 9+ messages in thread
From: Willem de Bruijn @ 2017-08-22 0:12 UTC (permalink / raw)
To: Eric Dumazet
Cc: Network Development, David Miller, Paolo Abeni, Willem de Bruijn
On Mon, Aug 21, 2017 at 6:40 PM, Eric Dumazet <eric.dumazet@gmail.com> wrote:
> On Mon, 2017-08-21 at 17:39 -0400, Willem de Bruijn wrote:
>> From: Willem de Bruijn <willemb@google.com>
>>
>> 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. With
>> sk_peek_off, the skb need not be at head, causing the call to fail and
>> the same skb to be found again on restart.
>>
>> Walk the queue to find the correct skb. Limit the walk to sk_peek_off,
>> to bound cycle cost to at most twice the original skb_queue_walk in
>> __skb_try_recv_from_queue.
>>
>> The operation may race with updates to sk_peek_off. As the operation
>> is retried, it will eventually succeed.
>>
>> Signed-off-by: Willem de Bruijn <willemb@google.com>
>
> You forgot the Fixes: tag, that such a bug fix deserves.
Indeed, sorry. I'm looking into that now. It should be the patch that
introduced peeking at offset, but need to verify.
I should also add that this bug was discovered by syzkaller.
> I am not a big fan of your patch and would prefer a solution without the
> loop.
Agreed.
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH net] udp: on peeking bad csum, drop packets even if not at head
2017-08-22 0:12 ` Willem de Bruijn
@ 2017-08-22 1:11 ` Willem de Bruijn
0 siblings, 0 replies; 9+ messages in thread
From: Willem de Bruijn @ 2017-08-22 1:11 UTC (permalink / raw)
To: Eric Dumazet
Cc: Network Development, David Miller, Paolo Abeni, Willem de Bruijn
On Mon, Aug 21, 2017 at 8:12 PM, Willem de Bruijn
<willemdebruijn.kernel@gmail.com> wrote:
> On Mon, Aug 21, 2017 at 6:40 PM, Eric Dumazet <eric.dumazet@gmail.com> wrote:
>> On Mon, 2017-08-21 at 17:39 -0400, Willem de Bruijn wrote:
>>> From: Willem de Bruijn <willemb@google.com>
>>>
>>> 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. With
>>> sk_peek_off, the skb need not be at head, causing the call to fail and
>>> the same skb to be found again on restart.
>>>
>>> Walk the queue to find the correct skb. Limit the walk to sk_peek_off,
>>> to bound cycle cost to at most twice the original skb_queue_walk in
>>> __skb_try_recv_from_queue.
>>>
>>> The operation may race with updates to sk_peek_off. As the operation
>>> is retried, it will eventually succeed.
>>>
>>> Signed-off-by: Willem de Bruijn <willemb@google.com>
>>
>> You forgot the Fixes: tag, that such a bug fix deserves.
>
> Indeed, sorry. I'm looking into that now. It should be the patch that
> introduced peeking at offset, but need to verify.
It is. Fixes: 627d2d6b5500 ("udp: enable MSG_PEEK at non-zero offset")
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v2 net] udp: on peeking bad csum, drop packets even if not at head
2017-08-21 22:40 ` Eric Dumazet
2017-08-22 0:12 ` Willem de Bruijn
@ 2017-08-22 16:39 ` Eric Dumazet
2017-08-22 16:47 ` Paolo Abeni
2017-08-22 21:28 ` David Miller
1 sibling, 2 replies; 9+ messages in thread
From: Eric Dumazet @ 2017-08-22 16:39 UTC (permalink / raw)
To: David Miller; +Cc: pabeni, Willem de Bruijn, netdev
From: Eric Dumazet <edumazet@google.com>
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>
---
net/core/datagram.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/core/datagram.c b/net/core/datagram.c
index a21ca8dee5ea..8c2f4489ff8f 100644
--- a/net/core/datagram.c
+++ b/net/core/datagram.c
@@ -362,7 +362,7 @@ int __sk_queue_drop_skb(struct sock *sk, struct sk_buff_head *sk_queue,
if (flags & MSG_PEEK) {
err = -ENOENT;
spin_lock_bh(&sk_queue->lock);
- if (skb == skb_peek(sk_queue)) {
+ if (skb->next) {
__skb_unlink(skb, sk_queue);
refcount_dec(&skb->users);
if (destructor)
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v2 net] udp: on peeking bad csum, drop packets even if not at head
2017-08-22 16:39 ` [PATCH v2 " Eric Dumazet
@ 2017-08-22 16:47 ` Paolo Abeni
2017-08-22 17:29 ` Willem de Bruijn
2017-08-22 21:28 ` David Miller
1 sibling, 1 reply; 9+ messages in thread
From: Paolo Abeni @ 2017-08-22 16:47 UTC (permalink / raw)
To: Eric Dumazet, David Miller; +Cc: Willem de Bruijn, netdev
On Tue, 2017-08-22 at 09:39 -0700, Eric Dumazet wrote:
> From: Eric Dumazet <edumazet@google.com>
>
> 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>
> ---
> net/core/datagram.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/net/core/datagram.c b/net/core/datagram.c
> index a21ca8dee5ea..8c2f4489ff8f 100644
> --- a/net/core/datagram.c
> +++ b/net/core/datagram.c
> @@ -362,7 +362,7 @@ int __sk_queue_drop_skb(struct sock *sk, struct sk_buff_head *sk_queue,
> if (flags & MSG_PEEK) {
> err = -ENOENT;
> spin_lock_bh(&sk_queue->lock);
> - if (skb == skb_peek(sk_queue)) {
> + if (skb->next) {
> __skb_unlink(skb, sk_queue);
> refcount_dec(&skb->users);
> if (destructor)
>
This version is really nice!
Acked-by: Paolo Abeni <pabeni@redhat.com>
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2 net] udp: on peeking bad csum, drop packets even if not at head
2017-08-22 16:47 ` Paolo Abeni
@ 2017-08-22 17:29 ` Willem de Bruijn
0 siblings, 0 replies; 9+ messages in thread
From: Willem de Bruijn @ 2017-08-22 17:29 UTC (permalink / raw)
To: Paolo Abeni; +Cc: Eric Dumazet, David Miller, Willem de Bruijn, netdev
On Tue, Aug 22, 2017 at 12:47 PM, Paolo Abeni <pabeni@redhat.com> wrote:
> On Tue, 2017-08-22 at 09:39 -0700, Eric Dumazet wrote:
>> From: Eric Dumazet <edumazet@google.com>
>>
>> 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>
>> ---
>> net/core/datagram.c | 2 +-
>> 1 file changed, 1 insertion(+), 1 deletion(-)
>>
>> diff --git a/net/core/datagram.c b/net/core/datagram.c
>> index a21ca8dee5ea..8c2f4489ff8f 100644
>> --- a/net/core/datagram.c
>> +++ b/net/core/datagram.c
>> @@ -362,7 +362,7 @@ int __sk_queue_drop_skb(struct sock *sk, struct sk_buff_head *sk_queue,
>> if (flags & MSG_PEEK) {
>> err = -ENOENT;
>> spin_lock_bh(&sk_queue->lock);
>> - if (skb == skb_peek(sk_queue)) {
>> + if (skb->next) {
>> __skb_unlink(skb, sk_queue);
>> refcount_dec(&skb->users);
>> if (destructor)
>>
>
> This version is really nice!
It is :) Thanks, Eric!
Acked-by: Willem de Bruijn <willemb@google.com>
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2 net] udp: on peeking bad csum, drop packets even if not at head
2017-08-22 16:39 ` [PATCH v2 " Eric Dumazet
2017-08-22 16:47 ` Paolo Abeni
@ 2017-08-22 21:28 ` David Miller
1 sibling, 0 replies; 9+ messages in thread
From: David Miller @ 2017-08-22 21:28 UTC (permalink / raw)
To: eric.dumazet; +Cc: pabeni, willemb, netdev
From: Eric Dumazet <eric.dumazet@gmail.com>
Date: Tue, 22 Aug 2017 09:39:28 -0700
> From: Eric Dumazet <edumazet@google.com>
>
> 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>
Applied and queued up for -stable.
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2017-08-22 21:28 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2017-08-21 21:39 [PATCH net] udp: on peeking bad csum, drop packets even if not at head Willem de Bruijn
2017-08-21 22:37 ` Willem de Bruijn
2017-08-21 22:40 ` Eric Dumazet
2017-08-22 0:12 ` Willem de Bruijn
2017-08-22 1:11 ` Willem de Bruijn
2017-08-22 16:39 ` [PATCH v2 " Eric Dumazet
2017-08-22 16:47 ` Paolo Abeni
2017-08-22 17:29 ` Willem de Bruijn
2017-08-22 21:28 ` David Miller
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).