mirror of
https://github.com/raspberrypi/linux.git
synced 2025-12-06 10:00:17 +00:00
ipv6: mcast: Check inet6_dev->dead under idev->mc_lock in __ipv6_dev_mc_inc().
[ Upstream commitdbd40f318c] Since commit63ed8de4be("mld: add mc_lock for protecting per-interface mld data"), every multicast resource is protected by inet6_dev->mc_lock. RTNL is unnecessary in terms of protection but still needed for synchronisation between addrconf_ifdown() and __ipv6_dev_mc_inc(). Once we removed RTNL, there would be a race below, where we could add a multicast address to a dead inet6_dev. CPU1 CPU2 ==== ==== addrconf_ifdown() __ipv6_dev_mc_inc() if (idev->dead) <-- false dead = true return -ENODEV; ipv6_mc_destroy_dev() / ipv6_mc_down() mutex_lock(&idev->mc_lock) ... mutex_unlock(&idev->mc_lock) mutex_lock(&idev->mc_lock) ... mutex_unlock(&idev->mc_lock) The race window can be easily closed by checking inet6_dev->dead under inet6_dev->mc_lock in __ipv6_dev_mc_inc() as addrconf_ifdown() will acquire it after marking inet6_dev dead. Let's check inet6_dev->dead under mc_lock in __ipv6_dev_mc_inc(). Note that now __ipv6_dev_mc_inc() no longer depends on RTNL and we can remove ASSERT_RTNL() there and the RTNL comment above addrconf_join_solict(). Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com> Reviewed-by: Eric Dumazet <edumazet@google.com> Link: https://patch.msgid.link/20250702230210.3115355-4-kuni1840@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org> Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
39195210fd
commit
2999361293
@@ -2229,13 +2229,12 @@ errdad:
|
|||||||
in6_ifa_put(ifp);
|
in6_ifa_put(ifp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Join to solicited addr multicast group.
|
/* Join to solicited addr multicast group. */
|
||||||
* caller must hold RTNL */
|
|
||||||
void addrconf_join_solict(struct net_device *dev, const struct in6_addr *addr)
|
void addrconf_join_solict(struct net_device *dev, const struct in6_addr *addr)
|
||||||
{
|
{
|
||||||
struct in6_addr maddr;
|
struct in6_addr maddr;
|
||||||
|
|
||||||
if (dev->flags&(IFF_LOOPBACK|IFF_NOARP))
|
if (READ_ONCE(dev->flags) & (IFF_LOOPBACK | IFF_NOARP))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
addrconf_addr_solict_mult(addr, &maddr);
|
addrconf_addr_solict_mult(addr, &maddr);
|
||||||
@@ -3860,7 +3859,7 @@ static int addrconf_ifdown(struct net_device *dev, bool unregister)
|
|||||||
* Do not dev_put!
|
* Do not dev_put!
|
||||||
*/
|
*/
|
||||||
if (unregister) {
|
if (unregister) {
|
||||||
idev->dead = 1;
|
WRITE_ONCE(idev->dead, 1);
|
||||||
|
|
||||||
/* protected by rtnl_lock */
|
/* protected by rtnl_lock */
|
||||||
RCU_INIT_POINTER(dev->ip6_ptr, NULL);
|
RCU_INIT_POINTER(dev->ip6_ptr, NULL);
|
||||||
|
|||||||
@@ -945,23 +945,22 @@ error:
|
|||||||
static int __ipv6_dev_mc_inc(struct net_device *dev,
|
static int __ipv6_dev_mc_inc(struct net_device *dev,
|
||||||
const struct in6_addr *addr, unsigned int mode)
|
const struct in6_addr *addr, unsigned int mode)
|
||||||
{
|
{
|
||||||
struct ifmcaddr6 *mc;
|
|
||||||
struct inet6_dev *idev;
|
struct inet6_dev *idev;
|
||||||
|
struct ifmcaddr6 *mc;
|
||||||
ASSERT_RTNL();
|
|
||||||
|
|
||||||
/* we need to take a reference on idev */
|
/* we need to take a reference on idev */
|
||||||
idev = in6_dev_get(dev);
|
idev = in6_dev_get(dev);
|
||||||
|
|
||||||
if (!idev)
|
if (!idev)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (idev->dead) {
|
mutex_lock(&idev->mc_lock);
|
||||||
|
|
||||||
|
if (READ_ONCE(idev->dead)) {
|
||||||
|
mutex_unlock(&idev->mc_lock);
|
||||||
in6_dev_put(idev);
|
in6_dev_put(idev);
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex_lock(&idev->mc_lock);
|
|
||||||
for_each_mc_mclock(idev, mc) {
|
for_each_mc_mclock(idev, mc) {
|
||||||
if (ipv6_addr_equal(&mc->mca_addr, addr)) {
|
if (ipv6_addr_equal(&mc->mca_addr, addr)) {
|
||||||
mc->mca_users++;
|
mc->mca_users++;
|
||||||
|
|||||||
Reference in New Issue
Block a user