There are a few cases where rtnl_net_lock() is not properly
held in rtnl_newlink().
When either of IFLA_NET_NS_PID / IFLA_NET_NS_FD / IFLA_TARGET_NETNSID
is specified but IFLA_LINK_NETNSID is not, sock_net(skb->sk) is used
as link_net in rtnl_newlink_link_net().
In addition, the do_setlink() path uses sock_net(skb->sk) and one
from the three netns attributes while rtnl_link_get_net_capable()
returns only one of four.
Let's add sock_net(skb->sk) to rtnl_nets in rtnl_newlink().
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
No fixes tag is needed since there is no real bug nor assertion.
---
net/core/rtnetlink.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 12aa3aa1688b..f39c93e80e20 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -282,10 +282,11 @@ static int rtnl_net_cmp_locks(const struct net *net_a, const struct net *net_b)
#endif
struct rtnl_nets {
- /* ->newlink() needs to freeze 3 netns at most;
- * 2 for the new device, 1 for its peer.
+ /* ->newlink() needs to freeze 4 netns at most;
+ * 2 for the new device, 1 for its peer, 1 for
+ * an existing device (do_setlink() path).
*/
- struct net *net[3];
+ struct net *net[4];
unsigned char len;
};
@@ -4155,6 +4156,8 @@ static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh,
}
}
+ rtnl_nets_add(&rtnl_nets, get_net(sock_net(skb->sk)));
+
rtnl_nets_lock(&rtnl_nets);
ret = __rtnl_newlink(skb, nlh, ops, tgt_net, link_net, peer_net, tbs, data, extack);
rtnl_nets_unlock(&rtnl_nets);
--
2.55.0.rc0.799.gd6f94ed593-goog