hsr: hold rcu and dev lock for hsr_get_port_ndev
authorHangbin Liu <liuhangbin@gmail.com>
Fri, 5 Sep 2025 09:15:33 +0000 (09:15 +0000)
committerPaolo Abeni <pabeni@redhat.com>
Thu, 11 Sep 2025 09:49:19 +0000 (11:49 +0200)
hsr_get_port_ndev calls hsr_for_each_port, which need to hold rcu lock.
On the other hand, before return the port device, we need to hold the
device reference to avoid UaF in the caller function.

Suggested-by: Paolo Abeni <pabeni@redhat.com>
Fixes: 9c10dd8eed74 ("net: hsr: Create and export hsr_get_port_ndev()")
Signed-off-by: Hangbin Liu <liuhangbin@gmail.com>
Reviewed-by: Simon Horman <horms@kernel.org>
Link: https://patch.msgid.link/20250905091533.377443-4-liuhangbin@gmail.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
drivers/net/ethernet/ti/icssg/icssg_prueth.c
net/hsr/hsr_device.c

index dadce6009791bc71402c99c1b8112f191276f335..e42d0fdefee1284033f740f871efe8f456487224 100644 (file)
@@ -654,7 +654,7 @@ static void icssg_prueth_hsr_fdb_add_del(struct prueth_emac *emac,
 
 static int icssg_prueth_hsr_add_mcast(struct net_device *ndev, const u8 *addr)
 {
-       struct net_device *real_dev;
+       struct net_device *real_dev, *port_dev;
        struct prueth_emac *emac;
        u8 vlan_id, i;
 
@@ -663,11 +663,15 @@ static int icssg_prueth_hsr_add_mcast(struct net_device *ndev, const u8 *addr)
 
        if (is_hsr_master(real_dev)) {
                for (i = HSR_PT_SLAVE_A; i < HSR_PT_INTERLINK; i++) {
-                       emac = netdev_priv(hsr_get_port_ndev(real_dev, i));
-                       if (!emac)
+                       port_dev = hsr_get_port_ndev(real_dev, i);
+                       emac = netdev_priv(port_dev);
+                       if (!emac) {
+                               dev_put(port_dev);
                                return -EINVAL;
+                       }
                        icssg_prueth_hsr_fdb_add_del(emac, addr, vlan_id,
                                                     true);
+                       dev_put(port_dev);
                }
        } else {
                emac = netdev_priv(real_dev);
@@ -679,7 +683,7 @@ static int icssg_prueth_hsr_add_mcast(struct net_device *ndev, const u8 *addr)
 
 static int icssg_prueth_hsr_del_mcast(struct net_device *ndev, const u8 *addr)
 {
-       struct net_device *real_dev;
+       struct net_device *real_dev, *port_dev;
        struct prueth_emac *emac;
        u8 vlan_id, i;
 
@@ -688,11 +692,15 @@ static int icssg_prueth_hsr_del_mcast(struct net_device *ndev, const u8 *addr)
 
        if (is_hsr_master(real_dev)) {
                for (i = HSR_PT_SLAVE_A; i < HSR_PT_INTERLINK; i++) {
-                       emac = netdev_priv(hsr_get_port_ndev(real_dev, i));
-                       if (!emac)
+                       port_dev = hsr_get_port_ndev(real_dev, i);
+                       emac = netdev_priv(port_dev);
+                       if (!emac) {
+                               dev_put(port_dev);
                                return -EINVAL;
+                       }
                        icssg_prueth_hsr_fdb_add_del(emac, addr, vlan_id,
                                                     false);
+                       dev_put(port_dev);
                }
        } else {
                emac = netdev_priv(real_dev);
index 702da1f9aaa90e2a3c12fba72d400846e60089f9..fbbc3ccf9df64b5022a45b32b3ce55f83b8d804b 100644 (file)
@@ -675,9 +675,14 @@ struct net_device *hsr_get_port_ndev(struct net_device *ndev,
        struct hsr_priv *hsr = netdev_priv(ndev);
        struct hsr_port *port;
 
+       rcu_read_lock();
        hsr_for_each_port(hsr, port)
-               if (port->type == pt)
+               if (port->type == pt) {
+                       dev_hold(port->dev);
+                       rcu_read_unlock();
                        return port->dev;
+               }
+       rcu_read_unlock();
        return NULL;
 }
 EXPORT_SYMBOL(hsr_get_port_ndev);