Skip to content

Commit d9b091d

Browse files
hartkoppmarckleinebudde
authored andcommitted
can: bcm: fix lockless bound/ifindex race and silent RX_SETUP failure
bcm_sendmsg() reads bo->ifindex and checks bo->bound before taking lock_sock(), while bcm_notify(), bcm_connect() and bcm_release() all mutate both fields under that same lock. Because the lockless reads and the locked writes are unordered with respect to each other, a racing bcm_notify() (device unregister) or bcm_connect() (concurrent bind on another thread sharing the socket) can make bcm_sendmsg() observe an inconsistent combination, e.g. a stale bound=1 together with the now-cleared ifindex=0, silently turning a socket bound to a specific CAN interface into one that also matches "any" interface. Keep the lockless bo->bound check purely as a fast-path reject, and move the ifindex read (and a bo->bound re-check) into the locked section, where every writer already serializes. This removes the possibility of observing the two fields torn against each other, rather than trying to fix it with more READ_ONCE()/WRITE_ONCE() pairs on two independently updated fields. Annotate the now-purely-lockless bo->bound accesses consistently across all its write sites. Also fix bcm_rx_setup() silently returning success when the target device disappears concurrently instead of reporting -ENODEV, so a broken RX op is no longer left registered as if it had succeeded. Fixes: ffd980f ("[CAN]: Add broadcast manager (bcm) protocol") Reported-by: Ginger <ginger.jzllee@gmail.com> Closes: https://lore.kernel.org/linux-can/CAGp+u1aBK8QVjsvAxM2Ldzep4rEbsP9x_pV3At4g=h1kVEtyhA@mail.gmail.com/ Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net> Link: https://patch.msgid.link/20260714-bcm_fixes-v15-2-562f7e3e42da@hartkopp.net Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
1 parent 68973f9 commit d9b091d

1 file changed

Lines changed: 51 additions & 14 deletions

File tree

net/can/bcm.c

Lines changed: 51 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1323,6 +1323,11 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
13231323

13241324
op->rx_reg_dev = dev;
13251325
dev_put(dev);
1326+
} else {
1327+
/* the requested device is gone - do not
1328+
* silently succeed without registering
1329+
*/
1330+
err = -ENODEV;
13261331
}
13271332

13281333
} else
@@ -1396,12 +1401,13 @@ static int bcm_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
13961401
{
13971402
struct sock *sk = sock->sk;
13981403
struct bcm_sock *bo = bcm_sk(sk);
1399-
int ifindex = bo->ifindex; /* default ifindex for this bcm_op */
1404+
int ifindex;
14001405
struct bcm_msg_head msg_head;
14011406
int cfsiz;
14021407
int ret; /* read bytes or error codes as return value */
14031408

1404-
if (!bo->bound)
1409+
/* Lockless fast-path check for bound socket */
1410+
if (!READ_ONCE(bo->bound))
14051411
return -ENOTCONN;
14061412

14071413
/* check for valid message length from userspace */
@@ -1417,17 +1423,38 @@ static int bcm_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
14171423
if ((size - MHSIZ) % cfsiz)
14181424
return -EINVAL;
14191425

1426+
lock_sock(sk);
1427+
1428+
/* Re-validate under the socket lock: a concurrent bcm_notify()
1429+
* may have unbound this socket (device removal) after the
1430+
* lockless fast-path check above. bo->ifindex is only ever
1431+
* mutated under lock_sock(), so reading it here - instead of
1432+
* before taking the lock - guarantees it can't be observed
1433+
* torn against bo->bound.
1434+
*/
1435+
if (!bo->bound) {
1436+
ret = -ENOTCONN;
1437+
goto out_release;
1438+
}
1439+
1440+
/* default ifindex for this bcm_op */
1441+
ifindex = bo->ifindex;
1442+
14201443
/* check for alternative ifindex for this bcm_op */
14211444

14221445
if (!ifindex && msg->msg_name) {
14231446
/* no bound device as default => check msg_name */
14241447
DECLARE_SOCKADDR(struct sockaddr_can *, addr, msg->msg_name);
14251448

1426-
if (msg->msg_namelen < BCM_MIN_NAMELEN)
1427-
return -EINVAL;
1449+
if (msg->msg_namelen < BCM_MIN_NAMELEN) {
1450+
ret = -EINVAL;
1451+
goto out_release;
1452+
}
14281453

1429-
if (addr->can_family != AF_CAN)
1430-
return -EINVAL;
1454+
if (addr->can_family != AF_CAN) {
1455+
ret = -EINVAL;
1456+
goto out_release;
1457+
}
14311458

14321459
/* ifindex from sendto() */
14331460
ifindex = addr->can_ifindex;
@@ -1436,20 +1463,21 @@ static int bcm_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
14361463
struct net_device *dev;
14371464

14381465
dev = dev_get_by_index(sock_net(sk), ifindex);
1439-
if (!dev)
1440-
return -ENODEV;
1466+
if (!dev) {
1467+
ret = -ENODEV;
1468+
goto out_release;
1469+
}
14411470

14421471
if (dev->type != ARPHRD_CAN) {
14431472
dev_put(dev);
1444-
return -ENODEV;
1473+
ret = -ENODEV;
1474+
goto out_release;
14451475
}
14461476

14471477
dev_put(dev);
14481478
}
14491479
}
14501480

1451-
lock_sock(sk);
1452-
14531481
switch (msg_head.opcode) {
14541482

14551483
case TX_SETUP:
@@ -1499,6 +1527,7 @@ static int bcm_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
14991527
break;
15001528
}
15011529

1530+
out_release:
15021531
release_sock(sk);
15031532

15041533
return ret;
@@ -1535,7 +1564,12 @@ static void bcm_notify(struct bcm_sock *bo, unsigned long msg,
15351564
bo->bcm_proc_read = NULL;
15361565
}
15371566
#endif
1538-
bo->bound = 0;
1567+
/* Paired with the lockless fast-path check in
1568+
* bcm_sendmsg(); bo->ifindex itself is only ever
1569+
* accessed under lock_sock() so it needs no
1570+
* annotation.
1571+
*/
1572+
WRITE_ONCE(bo->bound, 0);
15391573
bo->ifindex = 0;
15401574
notify_enodev = 1;
15411575
}
@@ -1676,7 +1710,7 @@ static int bcm_release(struct socket *sock)
16761710

16771711
/* remove device reference */
16781712
if (bo->bound) {
1679-
bo->bound = 0;
1713+
WRITE_ONCE(bo->bound, 0);
16801714
bo->ifindex = 0;
16811715
}
16821716

@@ -1746,7 +1780,10 @@ static int bcm_connect(struct socket *sock, struct sockaddr_unsized *uaddr, int
17461780
}
17471781
#endif /* CONFIG_PROC_FS */
17481782

1749-
bo->bound = 1;
1783+
/* bo->ifindex above is fully assigned before this point; pairs
1784+
* with the lockless fast-path check in bcm_sendmsg()
1785+
*/
1786+
WRITE_ONCE(bo->bound, 1);
17501787

17511788
fail:
17521789
release_sock(sk);

0 commit comments

Comments
 (0)