lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <174265435817.356712.639144257882603362.stgit@pro.pro>
Date: Sat, 22 Mar 2025 17:39:18 +0300
From: Kirill Tkhai <tkhai@...ru>
To: netdev@...r.kernel.org,
	linux-kernel@...r.kernel.org
Cc: tkhai@...ru
Subject: [PATCH NET-PREV 11/51] net: Make master and slaves (any dependent devices) share the same nd_lock in .setlink etc

Signed-off-by: Kirill Tkhai <tkhai@...ru>
---
 net/core/rtnetlink.c |  134 ++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 129 insertions(+), 5 deletions(-)

diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index cf060ba4cd1d..67b4b0610d14 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -2696,9 +2696,16 @@ static int do_set_master(struct net_device *dev, struct net_device *master,
 	}
 
 	if (master) {
+		struct nd_lock *nd_lock = rcu_access_pointer(dev->nd_lock);
+		struct nd_lock *nd_lock2 = rcu_access_pointer(master->nd_lock);
+
 		upper_dev = master;
 		ops = upper_dev->netdev_ops;
 		if (ops->ndo_add_slave) {
+			/* Devices linked as upper<->lower must relate
+			 * to the same nd_lock.
+			 */
+			nd_lock_transfer_devices(&nd_lock, &nd_lock2);
 			err = ops->ndo_add_slave(upper_dev, dev, extack);
 			if (err)
 				return err;
@@ -3173,6 +3180,7 @@ static int rtnl_setlink(struct sk_buff *skb, struct nlmsghdr *nlh,
 	struct net *net = sock_net(skb->sk);
 	struct ifinfomsg *ifm;
 	struct net_device *dev, *master = NULL;
+	struct nd_lock *nd_lock, *nd_lock2;
 	struct net *target_net = NULL;
 	int err;
 	struct nlattr *tb[IFLA_MAX+1];
@@ -3217,7 +3225,9 @@ static int rtnl_setlink(struct sk_buff *skb, struct nlmsghdr *nlh,
 	if (err < 0)
 		goto errout;
 
+	double_lock_netdev(dev, &nd_lock, master, &nd_lock2);
 	err = do_setlink(target_net, skb, dev, master, ifm, extack, tb, 0);
+	double_unlock_netdev(nd_lock, nd_lock2);
 errout:
 	if (target_net)
 		put_net(target_net);
@@ -3458,6 +3468,7 @@ static int rtnl_group_changelink(const struct sk_buff *skb,
 		struct nlattr **tb)
 {
 	struct net_device *dev, *aux, *master = NULL;
+	struct nd_lock *nd_lock, *nd_lock2;
 	struct net *target_net;
 	int err;
 
@@ -3479,7 +3490,9 @@ static int rtnl_group_changelink(const struct sk_buff *skb,
 			err = validate_linkmsg(dev, tb, extack);
 			if (err < 0)
 				break;
+			double_lock_netdev(dev, &nd_lock, master, &nd_lock2);
 			err = do_setlink(target_net, skb, dev, master, ifm, extack, tb, 0);
+			double_unlock_netdev(nd_lock, nd_lock2);
 			if (err < 0)
 				break;
 		}
@@ -3495,6 +3508,74 @@ struct link_deps generic_newlink_deps = {
 };
 EXPORT_SYMBOL_GPL(generic_newlink_deps);
 
+static struct net_device *__resolve_deps_locks(struct net *net,
+					       struct net_device *dev,
+					       struct nlattr **attr,
+					       const int deps[],
+					       bool mandatory)
+{
+	struct nd_lock *nd_lock, *nd_lock2;
+	struct net_device *dev2;
+	int i, key, ifindex;
+
+	for (i = 0; i <= MAX_LINK_DEPS; i++) {
+		key = deps[i];
+		if (!key)
+			break;
+		if (!attr[key]) {
+			if (mandatory)
+				return ERR_PTR(-ENODEV);
+			continue;
+		}
+		ifindex = nla_get_u32(attr[key]);
+
+		if (!dev) {
+			dev = __dev_get_by_index(net, ifindex);
+			if (!dev && mandatory)
+				return ERR_PTR(-ENODEV);
+			continue;
+		}
+
+		dev2 = __dev_get_by_index(net, ifindex);
+		if (!dev2) {
+			if (mandatory)
+				return ERR_PTR(-ENODEV);
+			continue;
+		}
+		double_lock_netdev(dev, &nd_lock, dev2, &nd_lock2);
+		nd_lock_transfer_devices(&nd_lock, &nd_lock2);
+		double_unlock_netdev(nd_lock, nd_lock2);
+	}
+
+	return dev;
+}
+
+/* Transfer all dependencies to the same nd_lock.
+ * Note, here we use that list of nd_lock devices
+ * can't be split in pieces.
+ */
+static struct net_device *resolve_deps_locks(struct net *net,
+					     const struct link_deps *deps,
+					     struct nlattr **tb,
+					     struct nlattr **data)
+{
+	struct net_device *dev = NULL;
+
+	if (!deps)
+		return NULL;
+
+	dev = __resolve_deps_locks(net, dev, tb, deps->mandatory.tb, true);
+	if (IS_ERR(dev))
+		return dev;
+	dev = __resolve_deps_locks(net, dev, data, deps->mandatory.data, true);
+	if (IS_ERR(dev))
+		return dev;
+	dev = __resolve_deps_locks(net, dev, tb, deps->optional.tb, false);
+	dev = __resolve_deps_locks(net, dev, tb, deps->optional.data, false);
+
+	return dev;
+}
+
 static int rtnl_newlink_create(struct sk_buff *skb, struct ifinfomsg *ifm,
 			       const struct rtnl_link_ops *ops,
 			       const struct nlmsghdr *nlh,
@@ -3506,7 +3587,8 @@ static int rtnl_newlink_create(struct sk_buff *skb, struct ifinfomsg *ifm,
 	struct net *net = sock_net(skb->sk);
 	u32 portid = NETLINK_CB(skb).portid;
 	struct net *link_net;
-	struct net_device *dev, *master = NULL;
+	struct net_device *dev, *master = NULL, *link_dev = NULL;
+	struct nd_lock *nd_lock, *nd_lock2;
 	char ifname[IFNAMSIZ];
 	LIST_HEAD(list_kill);
 	int err;
@@ -3554,13 +3636,36 @@ static int rtnl_newlink_create(struct sk_buff *skb, struct ifinfomsg *ifm,
 		goto out;
 	}
 
+	link_dev = resolve_deps_locks(link_net ? : net, ops->newlink_deps, tb, data);
+	if (IS_ERR(link_dev)) {
+		err = -EINVAL;
+		goto out;
+	}
+
+	if (master && link_dev) {
+		double_lock_netdev(master, &nd_lock, link_dev, &nd_lock2);
+		nd_lock_transfer_devices(&nd_lock, &nd_lock2);
+		if (nd_lock != nd_lock2)
+			unlock_netdev(nd_lock);
+	} else if (master || link_dev) {
+		lock_netdev(master ? : link_dev, &nd_lock);
+	} else {
+		nd_lock = alloc_nd_lock();
+		err = -ENOMEM;
+		if (!nd_lock)
+			goto out;
+		mutex_lock(&nd_lock->mutex);
+	}
+	attach_nd_lock(dev, nd_lock);
+
 	if (ops->newlink)
 		err = ops->newlink(link_net ? : net, dev, tb, data, extack);
 	else
-		err = register_netdevice(dev);
+		err = __register_netdevice(dev);
 	if (err < 0) {
+		detach_nd_lock(dev);
 		free_netdev(dev);
-		goto out;
+		goto unlock;
 	}
 
 	err = rtnl_configure_link(dev, ifm, portid, nlh);
@@ -3576,6 +3681,8 @@ static int rtnl_newlink_create(struct sk_buff *skb, struct ifinfomsg *ifm,
 		if (err)
 			goto out_unregister;
 	}
+unlock:
+	unlock_netdev(nd_lock);
 out:
 	if (link_net)
 		put_net(link_net);
@@ -3587,7 +3694,7 @@ static int rtnl_newlink_create(struct sk_buff *skb, struct ifinfomsg *ifm,
 		unregister_netdevice_queue(dev, &list_kill);
 	}
 	unregister_netdevice_many(&list_kill);
-	goto out;
+	goto unlock;
 }
 
 struct rtnl_newlink_tbs {
@@ -3608,7 +3715,8 @@ static int __rtnl_newlink_setlink(struct sk_buff *skb, struct nlmsghdr *nlh,
 	struct ifinfomsg *ifm = nlmsg_data(nlh);
 	struct nlattr ** const tb = tbs->tb;
 	struct nlattr **slave_data = NULL;
-	struct net_device *master_dev;
+	struct net_device *master_dev, *link_dev;
+	struct nd_lock *nd_lock, *nd_lock2;
 	int err, status = 0;
 
 	if (nlh->nlmsg_flags & NLM_F_EXCL)
@@ -3620,6 +3728,21 @@ static int __rtnl_newlink_setlink(struct sk_buff *skb, struct nlmsghdr *nlh,
 	if (err < 0)
 		return err;
 
+	if (ops && ops == dev->rtnl_link_ops && linkinfo[IFLA_INFO_DATA]) {
+		link_dev = resolve_deps_locks(dev_net(dev),
+					      ops->changelink_deps,
+					      tb, data);
+		if (IS_ERR(link_dev))
+			return PTR_ERR(link_dev);
+
+		if (link_dev) {
+			double_lock_netdev(dev, &nd_lock, link_dev, &nd_lock2);
+			nd_lock_transfer_devices(&nd_lock, &nd_lock2);
+			double_unlock_netdev(nd_lock, nd_lock2);
+		}
+	}
+
+	double_lock_netdev(dev, &nd_lock, new_master, &nd_lock2);
 	master_dev = netdev_master_upper_dev_get(dev);
 	if (master_dev)
 		m_ops = master_dev->rtnl_link_ops;
@@ -3668,6 +3791,7 @@ static int __rtnl_newlink_setlink(struct sk_buff *skb, struct nlmsghdr *nlh,
 
 	err = do_setlink(target_net, skb, dev, new_master, ifm, extack, tb, status);
 out:
+	double_unlock_netdev(nd_lock, nd_lock2);
 	return err;
 }
 


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ