diff mbox

[v2,net] udp: fix poll() issue with zero sized packets

Message ID 1471985973.14381.54.camel@edumazet-glaptop3.roam.corp.google.com
State Accepted, archived
Delegated to: David Miller
Headers show

Commit Message

Eric Dumazet Aug. 23, 2016, 8:59 p.m. UTC
From: Eric Dumazet <edumazet@google.com>

Laura tracked poll() [and friends] regression caused by commit
e6afc8ace6dd ("udp: remove headers from UDP packets before queueing")

udp_poll() needs to know if there is a valid packet in receive queue,
even if its payload length is 0.

Change first_packet_length() to return an signed int, and use -1
as the indication of an empty queue.

Fixes: e6afc8ace6dd ("udp: remove headers from UDP packets before queueing")
Reported-by: Laura Abbott <labbott@redhat.com>
Signed-off-by: Eric Dumazet <edumazet@google.com>
Tested-by: Laura Abbott <labbott@redhat.com>
---
v2: fix the comment/doc (Willem)

 net/ipv4/udp.c |   12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

Comments

David Miller Aug. 23, 2016, 11:39 p.m. UTC | #1
From: Eric Dumazet <eric.dumazet@gmail.com>
Date: Tue, 23 Aug 2016 13:59:33 -0700

> From: Eric Dumazet <edumazet@google.com>
> 
> Laura tracked poll() [and friends] regression caused by commit
> e6afc8ace6dd ("udp: remove headers from UDP packets before queueing")
> 
> udp_poll() needs to know if there is a valid packet in receive queue,
> even if its payload length is 0.
> 
> Change first_packet_length() to return an signed int, and use -1
> as the indication of an empty queue.
> 
> Fixes: e6afc8ace6dd ("udp: remove headers from UDP packets before queueing")
> Reported-by: Laura Abbott <labbott@redhat.com>
> Signed-off-by: Eric Dumazet <edumazet@google.com>
> Tested-by: Laura Abbott <labbott@redhat.com>
> ---
> v2: fix the comment/doc (Willem)

Applied and queued up for -stable, thanks.
diff mbox

Patch

diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index e61f7cd65d08..00d18c57c83c 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -1182,13 +1182,13 @@  out:
  *	@sk: socket
  *
  *	Drops all bad checksum frames, until a valid one is found.
- *	Returns the length of found skb, or 0 if none is found.
+ *	Returns the length of found skb, or -1 if none is found.
  */
-static unsigned int first_packet_length(struct sock *sk)
+static int first_packet_length(struct sock *sk)
 {
 	struct sk_buff_head list_kill, *rcvq = &sk->sk_receive_queue;
 	struct sk_buff *skb;
-	unsigned int res;
+	int res;
 
 	__skb_queue_head_init(&list_kill);
 
@@ -1203,7 +1203,7 @@  static unsigned int first_packet_length(struct sock *sk)
 		__skb_unlink(skb, rcvq);
 		__skb_queue_tail(&list_kill, skb);
 	}
-	res = skb ? skb->len : 0;
+	res = skb ? skb->len : -1;
 	spin_unlock_bh(&rcvq->lock);
 
 	if (!skb_queue_empty(&list_kill)) {
@@ -1232,7 +1232,7 @@  int udp_ioctl(struct sock *sk, int cmd, unsigned long arg)
 
 	case SIOCINQ:
 	{
-		unsigned int amount = first_packet_length(sk);
+		int amount = max_t(int, 0, first_packet_length(sk));
 
 		return put_user(amount, (int __user *)arg);
 	}
@@ -2184,7 +2184,7 @@  unsigned int udp_poll(struct file *file, struct socket *sock, poll_table *wait)
 
 	/* Check for false positives due to checksum errors */
 	if ((mask & POLLRDNORM) && !(file->f_flags & O_NONBLOCK) &&
-	    !(sk->sk_shutdown & RCV_SHUTDOWN) && !first_packet_length(sk))
+	    !(sk->sk_shutdown & RCV_SHUTDOWN) && first_packet_length(sk) == -1)
 		mask &= ~(POLLIN | POLLRDNORM);
 
 	return mask;