rtnetlink: Lock sock_net(skb->sk) in rtnl_newlink().

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>
Link: https://patch.msgid.link/20260703001009.1572444-2-kuniyu@google.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Kuniyuki Iwashima
2026-07-03 00:09:12 +00:00
committed by Paolo Abeni
parent 23dad2d088
commit 5ecbbb179e

View File

@@ -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;
};
@@ -4158,6 +4159,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);