Skip to content

Commit 42fddcc

Browse files
Björn Töpelborkmann
authored andcommitted
xsk: use state member for socket synchronization
Prior the state variable was introduced by Ilya, the dev member was used to determine whether the socket was bound or not. However, when dev was read, proper SMP barriers and READ_ONCE were missing. In order to address the missing barriers and READ_ONCE, we start using the state variable as a point of synchronization. The state member read/write is paired with proper SMP barriers, and from this follows that the members described above does not need READ_ONCE if used in conjunction with state check. In all syscalls and the xsk_rcv path we check if state is XSK_BOUND. If that is the case we do a SMP read barrier, and this implies that the dev, umem and all rings are correctly setup. Note that no READ_ONCE are needed for these variable if used when state is XSK_BOUND (plus the read barrier). To summarize: The members struct xdp_sock members dev, queue_id, umem, fq, cq, tx, rx, and state were read lock-less, with incorrect barriers and missing {READ, WRITE}_ONCE. Now, umem, fq, cq, tx, rx, and state are read lock-less. When these members are updated, WRITE_ONCE is used. When read, READ_ONCE are only used when read outside the control mutex (e.g. mmap) or, not synchronized with the state member (XSK_BOUND plus smp_rmb()) Note that dev and queue_id do not need a WRITE_ONCE or READ_ONCE, due to the introduce state synchronization (XSK_BOUND plus smp_rmb()). Introducing the state check also fixes a race, found by syzcaller, in xsk_poll() where umem could be accessed when stale. Suggested-by: Hillf Danton <hdanton@sina.com> Reported-by: syzbot+c82697e3043781e08802@syzkaller.appspotmail.com Fixes: 77cd0d7 ("xsk: add support for need_wakeup flag in AF_XDP rings") Signed-off-by: Björn Töpel <bjorn.topel@intel.com> Acked-by: Jonathan Lemon <jonathan.lemon@gmail.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
1 parent 9764f4b commit 42fddcc

File tree

1 file changed

+39
-15
lines changed

1 file changed

+39
-15
lines changed

net/xdp/xsk.c

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -186,10 +186,23 @@ static int __xsk_rcv_zc(struct xdp_sock *xs, struct xdp_buff *xdp, u32 len)
186186
return err;
187187
}
188188

189+
static bool xsk_is_bound(struct xdp_sock *xs)
190+
{
191+
if (READ_ONCE(xs->state) == XSK_BOUND) {
192+
/* Matches smp_wmb() in bind(). */
193+
smp_rmb();
194+
return true;
195+
}
196+
return false;
197+
}
198+
189199
int xsk_rcv(struct xdp_sock *xs, struct xdp_buff *xdp)
190200
{
191201
u32 len;
192202

203+
if (!xsk_is_bound(xs))
204+
return -EINVAL;
205+
193206
if (xs->dev != xdp->rxq->dev || xs->queue_id != xdp->rxq->queue_index)
194207
return -EINVAL;
195208

@@ -387,7 +400,7 @@ static int xsk_sendmsg(struct socket *sock, struct msghdr *m, size_t total_len)
387400
struct sock *sk = sock->sk;
388401
struct xdp_sock *xs = xdp_sk(sk);
389402

390-
if (unlikely(!xs->dev))
403+
if (unlikely(!xsk_is_bound(xs)))
391404
return -ENXIO;
392405
if (unlikely(!(xs->dev->flags & IFF_UP)))
393406
return -ENETDOWN;
@@ -403,10 +416,15 @@ static unsigned int xsk_poll(struct file *file, struct socket *sock,
403416
struct poll_table_struct *wait)
404417
{
405418
unsigned int mask = datagram_poll(file, sock, wait);
406-
struct sock *sk = sock->sk;
407-
struct xdp_sock *xs = xdp_sk(sk);
408-
struct net_device *dev = xs->dev;
409-
struct xdp_umem *umem = xs->umem;
419+
struct xdp_sock *xs = xdp_sk(sock->sk);
420+
struct net_device *dev;
421+
struct xdp_umem *umem;
422+
423+
if (unlikely(!xsk_is_bound(xs)))
424+
return mask;
425+
426+
dev = xs->dev;
427+
umem = xs->umem;
410428

411429
if (umem->need_wakeup)
412430
dev->netdev_ops->ndo_xsk_wakeup(dev, xs->queue_id,
@@ -442,10 +460,9 @@ static void xsk_unbind_dev(struct xdp_sock *xs)
442460
{
443461
struct net_device *dev = xs->dev;
444462

445-
if (!dev || xs->state != XSK_BOUND)
463+
if (xs->state != XSK_BOUND)
446464
return;
447-
448-
xs->state = XSK_UNBOUND;
465+
WRITE_ONCE(xs->state, XSK_UNBOUND);
449466

450467
/* Wait for driver to stop using the xdp socket. */
451468
xdp_del_sk_umem(xs->umem, xs);
@@ -520,7 +537,9 @@ static int xsk_release(struct socket *sock)
520537
local_bh_enable();
521538

522539
xsk_delete_from_maps(xs);
540+
mutex_lock(&xs->mutex);
523541
xsk_unbind_dev(xs);
542+
mutex_unlock(&xs->mutex);
524543

525544
xskq_destroy(xs->rx);
526545
xskq_destroy(xs->tx);
@@ -632,12 +651,12 @@ static int xsk_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
632651
}
633652

634653
umem_xs = xdp_sk(sock->sk);
635-
if (!umem_xs->umem) {
636-
/* No umem to inherit. */
654+
if (!xsk_is_bound(umem_xs)) {
637655
err = -EBADF;
638656
sockfd_put(sock);
639657
goto out_unlock;
640-
} else if (umem_xs->dev != dev || umem_xs->queue_id != qid) {
658+
}
659+
if (umem_xs->dev != dev || umem_xs->queue_id != qid) {
641660
err = -EINVAL;
642661
sockfd_put(sock);
643662
goto out_unlock;
@@ -671,10 +690,15 @@ static int xsk_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
671690
xdp_add_sk_umem(xs->umem, xs);
672691

673692
out_unlock:
674-
if (err)
693+
if (err) {
675694
dev_put(dev);
676-
else
677-
xs->state = XSK_BOUND;
695+
} else {
696+
/* Matches smp_rmb() in bind() for shared umem
697+
* sockets, and xsk_is_bound().
698+
*/
699+
smp_wmb();
700+
WRITE_ONCE(xs->state, XSK_BOUND);
701+
}
678702
out_release:
679703
mutex_unlock(&xs->mutex);
680704
rtnl_unlock();
@@ -927,7 +951,7 @@ static int xsk_mmap(struct file *file, struct socket *sock,
927951
unsigned long pfn;
928952
struct page *qpg;
929953

930-
if (xs->state != XSK_READY)
954+
if (READ_ONCE(xs->state) != XSK_READY)
931955
return -EBUSY;
932956

933957
if (offset == XDP_PGOFF_RX_RING) {

0 commit comments

Comments
 (0)