Skip to content

Commit

Permalink
netlink: rx mmap: fix POLLIN condition
Browse files Browse the repository at this point in the history
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 <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
chamaken authored and davem330 committed Aug 31, 2015
1 parent 793768f commit 0ef7077
Showing 1 changed file with 16 additions and 12 deletions.
28 changes: 16 additions & 12 deletions net/netlink/af_netlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit 0ef7077

Please sign in to comment.