Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

netlink: convert nlk->flags to atomic flags

sk_diag_put_flags(), netlink_setsockopt(), netlink_getsockopt()
and others use nlk->flags without correct locking.

Use set_bit(), clear_bit(), test_bit(), assign_bit() to remove
data-races.

Reported-by: syzbot <syzkaller@googlegroups.com>
Signed-off-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: Simon Horman <horms@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Eric Dumazet and committed by
David S. Miller
8fe08d70 86f03776

+48 -74
+30 -60
net/netlink/af_netlink.c
··· 84 84 85 85 static inline int netlink_is_kernel(struct sock *sk) 86 86 { 87 - return nlk_sk(sk)->flags & NETLINK_F_KERNEL_SOCKET; 87 + return nlk_test_bit(KERNEL_SOCKET, sk); 88 88 } 89 89 90 90 struct netlink_table *nl_table __read_mostly; ··· 349 349 350 350 static void netlink_overrun(struct sock *sk) 351 351 { 352 - struct netlink_sock *nlk = nlk_sk(sk); 353 - 354 - if (!(nlk->flags & NETLINK_F_RECV_NO_ENOBUFS)) { 352 + if (!nlk_test_bit(RECV_NO_ENOBUFS, sk)) { 355 353 if (!test_and_set_bit(NETLINK_S_CONGESTED, 356 354 &nlk_sk(sk)->state)) { 357 355 sk->sk_err = ENOBUFS; ··· 1405 1407 1406 1408 bool netlink_strict_get_check(struct sk_buff *skb) 1407 1409 { 1408 - const struct netlink_sock *nlk = nlk_sk(NETLINK_CB(skb).sk); 1409 - 1410 - return nlk->flags & NETLINK_F_STRICT_CHK; 1410 + return nlk_test_bit(STRICT_CHK, NETLINK_CB(skb).sk); 1411 1411 } 1412 1412 EXPORT_SYMBOL_GPL(netlink_strict_get_check); 1413 1413 ··· 1451 1455 return; 1452 1456 1453 1457 if (!net_eq(sock_net(sk), p->net)) { 1454 - if (!(nlk->flags & NETLINK_F_LISTEN_ALL_NSID)) 1458 + if (!nlk_test_bit(LISTEN_ALL_NSID, sk)) 1455 1459 return; 1456 1460 1457 1461 if (!peernet_has_id(sock_net(sk), p->net)) ··· 1484 1488 netlink_overrun(sk); 1485 1489 /* Clone failed. Notify ALL listeners. */ 1486 1490 p->failure = 1; 1487 - if (nlk->flags & NETLINK_F_BROADCAST_SEND_ERROR) 1491 + if (nlk_test_bit(BROADCAST_SEND_ERROR, sk)) 1488 1492 p->delivery_failure = 1; 1489 1493 goto out; 1490 1494 } ··· 1506 1510 val = netlink_broadcast_deliver(sk, p->skb2); 1507 1511 if (val < 0) { 1508 1512 netlink_overrun(sk); 1509 - if (nlk->flags & NETLINK_F_BROADCAST_SEND_ERROR) 1513 + if (nlk_test_bit(BROADCAST_SEND_ERROR, sk)) 1510 1514 p->delivery_failure = 1; 1511 1515 } else { 1512 1516 p->congested |= val; ··· 1600 1604 !test_bit(p->group - 1, nlk->groups)) 1601 1605 goto out; 1602 1606 1603 - if (p->code == ENOBUFS && nlk->flags & NETLINK_F_RECV_NO_ENOBUFS) { 1607 + if (p->code == ENOBUFS && nlk_test_bit(RECV_NO_ENOBUFS, sk)) { 1604 1608 ret = 1; 1605 1609 goto out; 1606 1610 } ··· 1664 1668 struct sock *sk = sock->sk; 1665 1669 struct netlink_sock *nlk = nlk_sk(sk); 1666 1670 unsigned int val = 0; 1667 - int err; 1671 + int nr = -1; 1668 1672 1669 1673 if (level != SOL_NETLINK) 1670 1674 return -ENOPROTOOPT; ··· 1675 1679 1676 1680 switch (optname) { 1677 1681 case NETLINK_PKTINFO: 1678 - if (val) 1679 - nlk->flags |= NETLINK_F_RECV_PKTINFO; 1680 - else 1681 - nlk->flags &= ~NETLINK_F_RECV_PKTINFO; 1682 - err = 0; 1682 + nr = NETLINK_F_RECV_PKTINFO; 1683 1683 break; 1684 1684 case NETLINK_ADD_MEMBERSHIP: 1685 1685 case NETLINK_DROP_MEMBERSHIP: { 1686 + int err; 1687 + 1686 1688 if (!netlink_allowed(sock, NL_CFG_F_NONROOT_RECV)) 1687 1689 return -EPERM; 1688 1690 err = netlink_realloc_groups(sk); ··· 1700 1706 if (optname == NETLINK_DROP_MEMBERSHIP && nlk->netlink_unbind) 1701 1707 nlk->netlink_unbind(sock_net(sk), val); 1702 1708 1703 - err = 0; 1704 1709 break; 1705 1710 } 1706 1711 case NETLINK_BROADCAST_ERROR: 1707 - if (val) 1708 - nlk->flags |= NETLINK_F_BROADCAST_SEND_ERROR; 1709 - else 1710 - nlk->flags &= ~NETLINK_F_BROADCAST_SEND_ERROR; 1711 - err = 0; 1712 + nr = NETLINK_F_BROADCAST_SEND_ERROR; 1712 1713 break; 1713 1714 case NETLINK_NO_ENOBUFS: 1715 + assign_bit(NETLINK_F_RECV_NO_ENOBUFS, &nlk->flags, val); 1714 1716 if (val) { 1715 - nlk->flags |= NETLINK_F_RECV_NO_ENOBUFS; 1716 1717 clear_bit(NETLINK_S_CONGESTED, &nlk->state); 1717 1718 wake_up_interruptible(&nlk->wait); 1718 - } else { 1719 - nlk->flags &= ~NETLINK_F_RECV_NO_ENOBUFS; 1720 1719 } 1721 - err = 0; 1722 1720 break; 1723 1721 case NETLINK_LISTEN_ALL_NSID: 1724 1722 if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_BROADCAST)) 1725 1723 return -EPERM; 1726 - 1727 - if (val) 1728 - nlk->flags |= NETLINK_F_LISTEN_ALL_NSID; 1729 - else 1730 - nlk->flags &= ~NETLINK_F_LISTEN_ALL_NSID; 1731 - err = 0; 1724 + nr = NETLINK_F_LISTEN_ALL_NSID; 1732 1725 break; 1733 1726 case NETLINK_CAP_ACK: 1734 - if (val) 1735 - nlk->flags |= NETLINK_F_CAP_ACK; 1736 - else 1737 - nlk->flags &= ~NETLINK_F_CAP_ACK; 1738 - err = 0; 1727 + nr = NETLINK_F_CAP_ACK; 1739 1728 break; 1740 1729 case NETLINK_EXT_ACK: 1741 - if (val) 1742 - nlk->flags |= NETLINK_F_EXT_ACK; 1743 - else 1744 - nlk->flags &= ~NETLINK_F_EXT_ACK; 1745 - err = 0; 1730 + nr = NETLINK_F_EXT_ACK; 1746 1731 break; 1747 1732 case NETLINK_GET_STRICT_CHK: 1748 - if (val) 1749 - nlk->flags |= NETLINK_F_STRICT_CHK; 1750 - else 1751 - nlk->flags &= ~NETLINK_F_STRICT_CHK; 1752 - err = 0; 1733 + nr = NETLINK_F_STRICT_CHK; 1753 1734 break; 1754 1735 default: 1755 - err = -ENOPROTOOPT; 1736 + return -ENOPROTOOPT; 1756 1737 } 1757 - return err; 1738 + if (nr >= 0) 1739 + assign_bit(nr, &nlk->flags, val); 1740 + return 0; 1758 1741 } 1759 1742 1760 1743 static int netlink_getsockopt(struct socket *sock, int level, int optname, ··· 1798 1827 return -EINVAL; 1799 1828 1800 1829 len = sizeof(int); 1801 - val = nlk->flags & flag ? 1 : 0; 1830 + val = test_bit(flag, &nlk->flags); 1802 1831 1803 1832 if (put_user(len, optlen) || 1804 1833 copy_to_user(optval, &val, len)) ··· 1975 2004 msg->msg_namelen = sizeof(*addr); 1976 2005 } 1977 2006 1978 - if (nlk->flags & NETLINK_F_RECV_PKTINFO) 2007 + if (nlk_test_bit(RECV_PKTINFO, sk)) 1979 2008 netlink_cmsg_recv_pktinfo(msg, skb); 1980 - if (nlk->flags & NETLINK_F_LISTEN_ALL_NSID) 2009 + if (nlk_test_bit(LISTEN_ALL_NSID, sk)) 1981 2010 netlink_cmsg_listen_all_nsid(sk, msg, skb); 1982 2011 1983 2012 memset(&scm, 0, sizeof(scm)); ··· 2054 2083 goto out_sock_release; 2055 2084 2056 2085 nlk = nlk_sk(sk); 2057 - nlk->flags |= NETLINK_F_KERNEL_SOCKET; 2086 + set_bit(NETLINK_F_KERNEL_SOCKET, &nlk->flags); 2058 2087 2059 2088 netlink_table_grab(); 2060 2089 if (!nl_table[unit].registered) { ··· 2189 2218 nl_dump_check_consistent(cb, nlh); 2190 2219 memcpy(nlmsg_data(nlh), &nlk->dump_done_errno, sizeof(nlk->dump_done_errno)); 2191 2220 2192 - if (extack->_msg && nlk->flags & NETLINK_F_EXT_ACK) { 2221 + if (extack->_msg && test_bit(NETLINK_F_EXT_ACK, &nlk->flags)) { 2193 2222 nlh->nlmsg_flags |= NLM_F_ACK_TLVS; 2194 2223 if (!nla_put_string(skb, NLMSGERR_ATTR_MSG, extack->_msg)) 2195 2224 nlmsg_end(skb, nlh); ··· 2318 2347 const struct nlmsghdr *nlh, 2319 2348 struct netlink_dump_control *control) 2320 2349 { 2321 - struct netlink_sock *nlk, *nlk2; 2322 2350 struct netlink_callback *cb; 2351 + struct netlink_sock *nlk; 2323 2352 struct sock *sk; 2324 2353 int ret; 2325 2354 ··· 2354 2383 cb->min_dump_alloc = control->min_dump_alloc; 2355 2384 cb->skb = skb; 2356 2385 2357 - nlk2 = nlk_sk(NETLINK_CB(skb).sk); 2358 - cb->strict_check = !!(nlk2->flags & NETLINK_F_STRICT_CHK); 2386 + cb->strict_check = nlk_test_bit(STRICT_CHK, NETLINK_CB(skb).sk); 2359 2387 2360 2388 if (control->start) { 2361 2389 cb->extack = control->extack; ··· 2398 2428 { 2399 2429 size_t tlvlen; 2400 2430 2401 - if (!extack || !(nlk->flags & NETLINK_F_EXT_ACK)) 2431 + if (!extack || !test_bit(NETLINK_F_EXT_ACK, &nlk->flags)) 2402 2432 return 0; 2403 2433 2404 2434 tlvlen = 0; ··· 2470 2500 * requests to cap the error message, and get extra error data if 2471 2501 * requested. 2472 2502 */ 2473 - if (err && !(nlk->flags & NETLINK_F_CAP_ACK)) 2503 + if (err && !test_bit(NETLINK_F_CAP_ACK, &nlk->flags)) 2474 2504 payload += nlmsg_len(nlh); 2475 2505 else 2476 2506 flags |= NLM_F_CAPPED;
+13 -9
net/netlink/af_netlink.h
··· 8 8 #include <net/sock.h> 9 9 10 10 /* flags */ 11 - #define NETLINK_F_KERNEL_SOCKET 0x1 12 - #define NETLINK_F_RECV_PKTINFO 0x2 13 - #define NETLINK_F_BROADCAST_SEND_ERROR 0x4 14 - #define NETLINK_F_RECV_NO_ENOBUFS 0x8 15 - #define NETLINK_F_LISTEN_ALL_NSID 0x10 16 - #define NETLINK_F_CAP_ACK 0x20 17 - #define NETLINK_F_EXT_ACK 0x40 18 - #define NETLINK_F_STRICT_CHK 0x80 11 + enum { 12 + NETLINK_F_KERNEL_SOCKET, 13 + NETLINK_F_RECV_PKTINFO, 14 + NETLINK_F_BROADCAST_SEND_ERROR, 15 + NETLINK_F_RECV_NO_ENOBUFS, 16 + NETLINK_F_LISTEN_ALL_NSID, 17 + NETLINK_F_CAP_ACK, 18 + NETLINK_F_EXT_ACK, 19 + NETLINK_F_STRICT_CHK, 20 + }; 19 21 20 22 #define NLGRPSZ(x) (ALIGN(x, sizeof(unsigned long) * 8) / 8) 21 23 #define NLGRPLONGS(x) (NLGRPSZ(x)/sizeof(unsigned long)) ··· 25 23 struct netlink_sock { 26 24 /* struct sock has to be the first member of netlink_sock */ 27 25 struct sock sk; 26 + unsigned long flags; 28 27 u32 portid; 29 28 u32 dst_portid; 30 29 u32 dst_group; 31 - u32 flags; 32 30 u32 subscriptions; 33 31 u32 ngroups; 34 32 unsigned long *groups; ··· 57 55 { 58 56 return container_of(sk, struct netlink_sock, sk); 59 57 } 58 + 59 + #define nlk_test_bit(nr, sk) test_bit(NETLINK_F_##nr, &nlk_sk(sk)->flags) 60 60 61 61 struct netlink_table { 62 62 struct rhashtable hash;
+5 -5
net/netlink/diag.c
··· 27 27 28 28 if (nlk->cb_running) 29 29 flags |= NDIAG_FLAG_CB_RUNNING; 30 - if (nlk->flags & NETLINK_F_RECV_PKTINFO) 30 + if (nlk_test_bit(RECV_PKTINFO, sk)) 31 31 flags |= NDIAG_FLAG_PKTINFO; 32 - if (nlk->flags & NETLINK_F_BROADCAST_SEND_ERROR) 32 + if (nlk_test_bit(BROADCAST_SEND_ERROR, sk)) 33 33 flags |= NDIAG_FLAG_BROADCAST_ERROR; 34 - if (nlk->flags & NETLINK_F_RECV_NO_ENOBUFS) 34 + if (nlk_test_bit(RECV_NO_ENOBUFS, sk)) 35 35 flags |= NDIAG_FLAG_NO_ENOBUFS; 36 - if (nlk->flags & NETLINK_F_LISTEN_ALL_NSID) 36 + if (nlk_test_bit(LISTEN_ALL_NSID, sk)) 37 37 flags |= NDIAG_FLAG_LISTEN_ALL_NSID; 38 - if (nlk->flags & NETLINK_F_CAP_ACK) 38 + if (nlk_test_bit(CAP_ACK, sk)) 39 39 flags |= NDIAG_FLAG_CAP_ACK; 40 40 41 41 return nla_put_u32(skb, NETLINK_DIAG_FLAGS, flags);