aboutsummaryrefslogtreecommitdiffstats
path: root/net/netlink
diff options
context:
space:
mode:
authorKen-ichirou MATSUZAWA <chamaken@gmail.com>2015-08-30 18:54:49 -0400
committerDavid S. Miller <davem@davemloft.net>2015-08-31 00:55:51 -0400
commit0ef707700f1cef2357ce655fc86a4de5e41fa4b5 (patch)
tree14962df2ccafdd0b86155d18b40cddd17d9e70a8 /net/netlink
parent793768f55c218a260015fe2029ae3d84676cae03 (diff)
netlink: rx mmap: fix POLLIN condition
Poll() returns immediately after setting the kernel current frame (ring->head) to SKIP from user space even though there is no new frame. And in a case of all frames is VALID, user space program unintensionally sets (only) kernel current frame to UNUSED, then calls poll(), it will not return immediately even though there are VALID frames. To avoid situations like above, I think we need to scan all frames to find VALID frames at poll() like netlink_alloc_skb(), netlink_forward_ring() finding an UNUSED frame at skb allocation. Signed-off-by: Ken-ichirou MATSUZAWA <chamas@h4.dion.ne.jp> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/netlink')
-rw-r--r--net/netlink/af_netlink.c28
1 files changed, 16 insertions, 12 deletions
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index 7965ca7c461d..50889be1517d 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -594,16 +594,6 @@ netlink_current_frame(const struct netlink_ring *ring,
594 return netlink_lookup_frame(ring, ring->head, status); 594 return netlink_lookup_frame(ring, ring->head, status);
595} 595}
596 596
597static struct nl_mmap_hdr *
598netlink_previous_frame(const struct netlink_ring *ring,
599 enum nl_mmap_status status)
600{
601 unsigned int prev;
602
603 prev = ring->head ? ring->head - 1 : ring->frame_max;
604 return netlink_lookup_frame(ring, prev, status);
605}
606
607static void netlink_increment_head(struct netlink_ring *ring) 597static void netlink_increment_head(struct netlink_ring *ring)
608{ 598{
609 ring->head = ring->head != ring->frame_max ? ring->head + 1 : 0; 599 ring->head = ring->head != ring->frame_max ? ring->head + 1 : 0;
@@ -624,6 +614,21 @@ static void netlink_forward_ring(struct netlink_ring *ring)
624 } while (ring->head != head); 614 } while (ring->head != head);
625} 615}
626 616
617static bool netlink_has_valid_frame(struct netlink_ring *ring)
618{
619 unsigned int head = ring->head, pos = head;
620 const struct nl_mmap_hdr *hdr;
621
622 do {
623 hdr = __netlink_lookup_frame(ring, pos);
624 if (hdr->nm_status == NL_MMAP_STATUS_VALID)
625 return true;
626 pos = pos != 0 ? pos - 1 : ring->frame_max;
627 } while (pos != head);
628
629 return false;
630}
631
627static bool netlink_dump_space(struct netlink_sock *nlk) 632static bool netlink_dump_space(struct netlink_sock *nlk)
628{ 633{
629 struct netlink_ring *ring = &nlk->rx_ring; 634 struct netlink_ring *ring = &nlk->rx_ring;
@@ -671,8 +676,7 @@ static unsigned int netlink_poll(struct file *file, struct socket *sock,
671 676
672 spin_lock_bh(&sk->sk_receive_queue.lock); 677 spin_lock_bh(&sk->sk_receive_queue.lock);
673 if (nlk->rx_ring.pg_vec) { 678 if (nlk->rx_ring.pg_vec) {
674 netlink_forward_ring(&nlk->rx_ring); 679 if (netlink_has_valid_frame(&nlk->rx_ring))
675 if (!netlink_previous_frame(&nlk->rx_ring, NL_MMAP_STATUS_UNUSED))
676 mask |= POLLIN | POLLRDNORM; 680 mask |= POLLIN | POLLRDNORM;
677 } 681 }
678 spin_unlock_bh(&sk->sk_receive_queue.lock); 682 spin_unlock_bh(&sk->sk_receive_queue.lock);