OSDN Git Service

rtnetlink: avoid RCU read lock when holding RTNL
authorCong Wang <cong.wang@bytedance.com>
Sat, 8 May 2021 18:00:33 +0000 (11:00 -0700)
committerDavid S. Miller <davem@davemloft.net>
Mon, 10 May 2021 21:33:10 +0000 (14:33 -0700)
When we call af_ops->set_link_af() we hold a RCU read lock
as we retrieve af_ops from the RCU protected list, but this
is unnecessary because we already hold RTNL lock, which is
the writer lock for protecting rtnl_af_ops, so it is safer
than RCU read lock. Similar for af_ops->validate_link_af().

This was not a problem until we begin to take mutex lock
down the path of ->set_link_af() in __ipv6_dev_mc_dec()
recently. We can just drop the RCU read lock there and
assert RTNL lock.

Reported-and-tested-by: syzbot+7d941e89dd48bcf42573@syzkaller.appspotmail.com
Fixes: 63ed8de4be81 ("mld: add mc_lock for protecting per-interface mld data")
Tested-by: Taehee Yoo <ap420073@gmail.com>
Signed-off-by: Cong Wang <cong.wang@bytedance.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/core/rtnetlink.c
net/ipv4/devinet.c

index 714d5fa..04b4f0f 100644 (file)
@@ -543,7 +543,9 @@ static const struct rtnl_af_ops *rtnl_af_lookup(const int family)
 {
        const struct rtnl_af_ops *ops;
 
-       list_for_each_entry_rcu(ops, &rtnl_af_ops, list) {
+       ASSERT_RTNL();
+
+       list_for_each_entry(ops, &rtnl_af_ops, list) {
                if (ops->family == family)
                        return ops;
        }
@@ -2274,27 +2276,18 @@ static int validate_linkmsg(struct net_device *dev, struct nlattr *tb[])
                nla_for_each_nested(af, tb[IFLA_AF_SPEC], rem) {
                        const struct rtnl_af_ops *af_ops;
 
-                       rcu_read_lock();
                        af_ops = rtnl_af_lookup(nla_type(af));
-                       if (!af_ops) {
-                               rcu_read_unlock();
+                       if (!af_ops)
                                return -EAFNOSUPPORT;
-                       }
 
-                       if (!af_ops->set_link_af) {
-                               rcu_read_unlock();
+                       if (!af_ops->set_link_af)
                                return -EOPNOTSUPP;
-                       }
 
                        if (af_ops->validate_link_af) {
                                err = af_ops->validate_link_af(dev, af);
-                               if (err < 0) {
-                                       rcu_read_unlock();
+                               if (err < 0)
                                        return err;
-                               }
                        }
-
-                       rcu_read_unlock();
                }
        }
 
@@ -2868,17 +2861,12 @@ static int do_setlink(const struct sk_buff *skb,
                nla_for_each_nested(af, tb[IFLA_AF_SPEC], rem) {
                        const struct rtnl_af_ops *af_ops;
 
-                       rcu_read_lock();
-
                        BUG_ON(!(af_ops = rtnl_af_lookup(nla_type(af))));
 
                        err = af_ops->set_link_af(dev, af, extack);
-                       if (err < 0) {
-                               rcu_read_unlock();
+                       if (err < 0)
                                goto errout;
-                       }
 
-                       rcu_read_unlock();
                        status |= DO_SETLINK_NOTIFY;
                }
        }
index 2e35f68..50deeff 100644 (file)
@@ -1955,7 +1955,7 @@ static int inet_validate_link_af(const struct net_device *dev,
        struct nlattr *a, *tb[IFLA_INET_MAX+1];
        int err, rem;
 
-       if (dev && !__in_dev_get_rcu(dev))
+       if (dev && !__in_dev_get_rtnl(dev))
                return -EAFNOSUPPORT;
 
        err = nla_parse_nested_deprecated(tb, IFLA_INET_MAX, nla,
@@ -1981,7 +1981,7 @@ static int inet_validate_link_af(const struct net_device *dev,
 static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla,
                            struct netlink_ext_ack *extack)
 {
-       struct in_device *in_dev = __in_dev_get_rcu(dev);
+       struct in_device *in_dev = __in_dev_get_rtnl(dev);
        struct nlattr *a, *tb[IFLA_INET_MAX+1];
        int rem;