Skip to content

Commit 8558467

Browse files
Eric Dumazetdavem330
authored andcommitted
udp: Fix udp_poll() and ioctl()
udp_poll() can in some circumstances drop frames with incorrect checksums. Problem is we now have to lock the socket while dropping frames, or risk sk_forward corruption. This bug is present since commit 95766ff ([UDP]: Add memory accounting.) While we are at it, we can correct ioctl(SIOCINQ) to also drop bad frames. Signed-off-by: Eric Dumazet <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 9652041 commit 8558467

File tree

1 file changed

+43
-30
lines changed

1 file changed

+43
-30
lines changed

net/ipv4/udp.c

Lines changed: 43 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -841,6 +841,42 @@ int udp_sendpage(struct sock *sk, struct page *page, int offset,
841841
return ret;
842842
}
843843

844+
845+
/**
846+
* first_packet_length - return length of first packet in receive queue
847+
* @sk: socket
848+
*
849+
* Drops all bad checksum frames, until a valid one is found.
850+
* Returns the length of found skb, or 0 if none is found.
851+
*/
852+
static unsigned int first_packet_length(struct sock *sk)
853+
{
854+
struct sk_buff_head list_kill, *rcvq = &sk->sk_receive_queue;
855+
struct sk_buff *skb;
856+
unsigned int res;
857+
858+
__skb_queue_head_init(&list_kill);
859+
860+
spin_lock_bh(&rcvq->lock);
861+
while ((skb = skb_peek(rcvq)) != NULL &&
862+
udp_lib_checksum_complete(skb)) {
863+
UDP_INC_STATS_BH(sock_net(sk), UDP_MIB_INERRORS,
864+
IS_UDPLITE(sk));
865+
__skb_unlink(skb, rcvq);
866+
__skb_queue_tail(&list_kill, skb);
867+
}
868+
res = skb ? skb->len : 0;
869+
spin_unlock_bh(&rcvq->lock);
870+
871+
if (!skb_queue_empty(&list_kill)) {
872+
lock_sock(sk);
873+
__skb_queue_purge(&list_kill);
874+
sk_mem_reclaim_partial(sk);
875+
release_sock(sk);
876+
}
877+
return res;
878+
}
879+
844880
/*
845881
* IOCTL requests applicable to the UDP protocol
846882
*/
@@ -857,21 +893,16 @@ int udp_ioctl(struct sock *sk, int cmd, unsigned long arg)
857893

858894
case SIOCINQ:
859895
{
860-
struct sk_buff *skb;
861-
unsigned long amount;
896+
unsigned int amount = first_packet_length(sk);
862897

863-
amount = 0;
864-
spin_lock_bh(&sk->sk_receive_queue.lock);
865-
skb = skb_peek(&sk->sk_receive_queue);
866-
if (skb != NULL) {
898+
if (amount)
867899
/*
868900
* We will only return the amount
869901
* of this packet since that is all
870902
* that will be read.
871903
*/
872-
amount = skb->len - sizeof(struct udphdr);
873-
}
874-
spin_unlock_bh(&sk->sk_receive_queue.lock);
904+
amount -= sizeof(struct udphdr);
905+
875906
return put_user(amount, (int __user *)arg);
876907
}
877908

@@ -1540,29 +1571,11 @@ unsigned int udp_poll(struct file *file, struct socket *sock, poll_table *wait)
15401571
{
15411572
unsigned int mask = datagram_poll(file, sock, wait);
15421573
struct sock *sk = sock->sk;
1543-
int is_lite = IS_UDPLITE(sk);
15441574

15451575
/* Check for false positives due to checksum errors */
1546-
if ((mask & POLLRDNORM) &&
1547-
!(file->f_flags & O_NONBLOCK) &&
1548-
!(sk->sk_shutdown & RCV_SHUTDOWN)) {
1549-
struct sk_buff_head *rcvq = &sk->sk_receive_queue;
1550-
struct sk_buff *skb;
1551-
1552-
spin_lock_bh(&rcvq->lock);
1553-
while ((skb = skb_peek(rcvq)) != NULL &&
1554-
udp_lib_checksum_complete(skb)) {
1555-
UDP_INC_STATS_BH(sock_net(sk),
1556-
UDP_MIB_INERRORS, is_lite);
1557-
__skb_unlink(skb, rcvq);
1558-
kfree_skb(skb);
1559-
}
1560-
spin_unlock_bh(&rcvq->lock);
1561-
1562-
/* nothing to see, move along */
1563-
if (skb == NULL)
1564-
mask &= ~(POLLIN | POLLRDNORM);
1565-
}
1576+
if ((mask & POLLRDNORM) && !(file->f_flags & O_NONBLOCK) &&
1577+
!(sk->sk_shutdown & RCV_SHUTDOWN) && !first_packet_length(sk))
1578+
mask &= ~(POLLIN | POLLRDNORM);
15661579

15671580
return mask;
15681581

0 commit comments

Comments
 (0)