summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorCong Wang <cong.wang@bytedance.com>2021-05-08 20:00:33 +0200
committerDavid S. Miller <davem@davemloft.net>2021-05-10 23:33:10 +0200
commita100243d95a60d74ae9bb9df1f5f2192e9aed6a7 (patch)
tree5c614edec5ccc729eae58fb83e8ef43026f9e9ad /net
parentMerge branch 'qca_spi-sync' (diff)
downloadlinux-a100243d95a60d74ae9bb9df1f5f2192e9aed6a7.tar.xz
linux-a100243d95a60d74ae9bb9df1f5f2192e9aed6a7.zip
rtnetlink: avoid RCU read lock when holding RTNL
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>
Diffstat (limited to 'net')
-rw-r--r--net/core/rtnetlink.c26
-rw-r--r--net/ipv4/devinet.c4
2 files changed, 9 insertions, 21 deletions
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 714d5fa38546..04b4f0f2a3d2 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -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;
}
}
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c
index 2e35f68da40a..50deeff48c8b 100644
--- a/net/ipv4/devinet.c
+++ b/net/ipv4/devinet.c
@@ -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;