mirror of
https://github.com/torvalds/linux.git
synced 2026-11-06 21:58:01 +01:00
ipv4: use RCU protection in inet_netconf_get_devconf()
inet_netconf_get_devconf() runs without RTNL. It uses dev_get_by_index() and in_dev_get() to pin the device and its in_device while the reply is allocated and filled. in_dev_get() is not safe for a lockless caller. Since commit9d40c84cf5("net: devinet: Reduce refcount before grace period"), inetdev_destroy() drops the final reference before the RCU grace period. A reader can load dev->ip_ptr, then call refcount_inc() on a zero refcount after in_dev_free_rcu() has been queued, and keep using the in_device once it has been freed. None of these references are needed. An in_device is freed after an RCU grace period, ipv4_devconf is embedded in it, and inet_netconf_fill_devconf() does not sleep. Allocate the skb first, then perform the lookup and the fill under rcu_read_lock(), using dev_get_by_index_rcu() and __in_dev_get_rcu(), like inet_netconf_dump_devconf() already does. Fixes:bbcf91053b("inet: do not use RTNL in inet_netconf_get_devconf()") Reported-by: Baul Lee <baul.lee@xbow.com> Closes: https://lore.kernel.org/netdev/20260815172032.79740-1-baul.lee@xbow.com/ Signed-off-by: Eric Dumazet <edumazet@kernel.org> Tested-by: Cen Zhang <zzzccc427@gmail.com> Reviewed-by: Ido Schimmel <idosch@nvidia.com> Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com> Link: https://patch.msgid.link/CAAVpQUA5FEMWV1R4vy2+uzQc+_iLDaz4m-D6kfKYngGf2sE0Eg@mail.gmail.com Link: https://patch.msgid.link/20261009074906.729877-1-edumazet@kernel.org Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
4fc1d5bb44
commit
b2039a6dd3
|
|
@ -2379,8 +2379,8 @@ static int inet_netconf_get_devconf(struct sk_buff *in_skb,
|
|||
struct net *net = sock_net(in_skb->sk);
|
||||
struct nlattr *tb[NETCONFA_MAX + 1];
|
||||
const struct ipv4_devconf *devconf;
|
||||
struct in_device *in_dev = NULL;
|
||||
struct net_device *dev = NULL;
|
||||
struct in_device *in_dev;
|
||||
struct net_device *dev;
|
||||
struct sk_buff *skb;
|
||||
int ifindex;
|
||||
int err;
|
||||
|
|
@ -2392,7 +2392,13 @@ static int inet_netconf_get_devconf(struct sk_buff *in_skb,
|
|||
if (!tb[NETCONFA_IFINDEX])
|
||||
return -EINVAL;
|
||||
|
||||
skb = nlmsg_new(inet_netconf_msgsize_devconf(NETCONFA_ALL), GFP_KERNEL);
|
||||
if (!skb)
|
||||
return -ENOBUFS;
|
||||
|
||||
ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]);
|
||||
|
||||
rcu_read_lock();
|
||||
switch (ifindex) {
|
||||
case NETCONFA_IFINDEX_ALL:
|
||||
devconf = net->ipv4.devconf_all;
|
||||
|
|
@ -2402,36 +2408,29 @@ static int inet_netconf_get_devconf(struct sk_buff *in_skb,
|
|||
break;
|
||||
default:
|
||||
err = -ENODEV;
|
||||
dev = dev_get_by_index(net, ifindex);
|
||||
if (dev)
|
||||
in_dev = in_dev_get(dev);
|
||||
dev = dev_get_by_index_rcu(net, ifindex);
|
||||
if (!dev)
|
||||
goto out_unlock;
|
||||
in_dev = __in_dev_get_rcu(dev);
|
||||
if (!in_dev)
|
||||
goto errout;
|
||||
goto out_unlock;
|
||||
devconf = &in_dev->cnf;
|
||||
break;
|
||||
}
|
||||
|
||||
err = -ENOBUFS;
|
||||
skb = nlmsg_new(inet_netconf_msgsize_devconf(NETCONFA_ALL), GFP_KERNEL);
|
||||
if (!skb)
|
||||
goto errout;
|
||||
|
||||
err = inet_netconf_fill_devconf(skb, ifindex, devconf,
|
||||
NETLINK_CB(in_skb).portid,
|
||||
nlh->nlmsg_seq, RTM_NEWNETCONF, 0,
|
||||
NETCONFA_ALL);
|
||||
out_unlock:
|
||||
rcu_read_unlock();
|
||||
if (err < 0) {
|
||||
/* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */
|
||||
WARN_ON(err == -EMSGSIZE);
|
||||
kfree_skb(skb);
|
||||
goto errout;
|
||||
return err;
|
||||
}
|
||||
err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
|
||||
errout:
|
||||
if (in_dev)
|
||||
in_dev_put(in_dev);
|
||||
dev_put(dev);
|
||||
return err;
|
||||
return rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
|
||||
}
|
||||
|
||||
static int inet_netconf_dump_devconf(struct sk_buff *skb,
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user