summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKen-ichirou MATSUZAWA <chamaken@gmail.com>2015-08-31 07:54:49 +0900
committerDavid S. Miller <davem@davemloft.net>2015-08-30 21:55:51 -0700
commit0ef707700f1cef2357ce655fc86a4de5e41fa4b5 (patch)
tree14962df2ccafdd0b86155d18b40cddd17d9e70a8
parent793768f55c218a260015fe2029ae3d84676cae03 (diff)
downloadlinux-stable-0ef707700f1cef2357ce655fc86a4de5e41fa4b5.tar.gz
linux-stable-0ef707700f1cef2357ce655fc86a4de5e41fa4b5.tar.bz2
linux-stable-0ef707700f1cef2357ce655fc86a4de5e41fa4b5.zip
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>
-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,
return netlink_lookup_frame(ring, ring->head, status);
}
-static struct nl_mmap_hdr *
-netlink_previous_frame(const struct netlink_ring *ring,
- enum nl_mmap_status status)
-{
- unsigned int prev;
-
- prev = ring->head ? ring->head - 1 : ring->frame_max;
- return netlink_lookup_frame(ring, prev, status);
-}
-
static void netlink_increment_head(struct netlink_ring *ring)
{
ring->head = ring->head != ring->frame_max ? ring->head + 1 : 0;
@@ -624,6 +614,21 @@ static void netlink_forward_ring(struct netlink_ring *ring)
} while (ring->head != head);
}
+static bool netlink_has_valid_frame(struct netlink_ring *ring)
+{
+ unsigned int head = ring->head, pos = head;
+ const struct nl_mmap_hdr *hdr;
+
+ do {
+ hdr = __netlink_lookup_frame(ring, pos);
+ if (hdr->nm_status == NL_MMAP_STATUS_VALID)
+ return true;
+ pos = pos != 0 ? pos - 1 : ring->frame_max;
+ } while (pos != head);
+
+ return false;
+}
+
static bool netlink_dump_space(struct netlink_sock *nlk)
{
struct netlink_ring *ring = &nlk->rx_ring;
@@ -671,8 +676,7 @@ static unsigned int netlink_poll(struct file *file, struct socket *sock,
spin_lock_bh(&sk->sk_receive_queue.lock);
if (nlk->rx_ring.pg_vec) {
- netlink_forward_ring(&nlk->rx_ring);
- if (!netlink_previous_frame(&nlk->rx_ring, NL_MMAP_STATUS_UNUSED))
+ if (netlink_has_valid_frame(&nlk->rx_ring))
mask |= POLLIN | POLLRDNORM;
}
spin_unlock_bh(&sk->sk_receive_queue.lock);