When the switch driver (e.g., mlxsw_spectrum) determines it needs to
flash a new firmware version it resets the ASIC after the flashing
process. The bus driver (e.g., mlxsw_pci) then registers itself again
with mlxsw_core which means (among other things) that the device
registers itself again with the hwmon subsystem again.

Since the device was registered with the hwmon subsystem using
devm_hwmon_device_register_with_groups(), then the old hwmon device
(registered before the flashing) was never unregistered and was
referencing stale data, resulting in a use-after free.

Fix by removing reliance on device managed APIs in mlxsw_hwmon_init().

Fixes: c86d62cc410c ("mlxsw: spectrum: Reset FW after flash")
Signed-off-by: Ido Schimmel <ido...@mellanox.com>
Reported-by: Alexander Petrovskiy <ale...@mellanox.com>
Tested-by: Alexander Petrovskiy <ale...@mellanox.com>
Reviewed-by: Petr Machata <pe...@mellanox.com>
---
 drivers/net/ethernet/mellanox/mlxsw/core.c      |  2 ++
 drivers/net/ethernet/mellanox/mlxsw/core.h      |  4 ++++
 .../net/ethernet/mellanox/mlxsw/core_hwmon.c    | 17 +++++++++++------
 3 files changed, 17 insertions(+), 6 deletions(-)

diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c 
b/drivers/net/ethernet/mellanox/mlxsw/core.c
index 81533d7f395c..937d0ace699a 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/core.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/core.c
@@ -1055,6 +1055,7 @@ int mlxsw_core_bus_device_register(const struct 
mlxsw_bus_info *mlxsw_bus_info,
 err_driver_init:
        mlxsw_thermal_fini(mlxsw_core->thermal);
 err_thermal_init:
+       mlxsw_hwmon_fini(mlxsw_core->hwmon);
 err_hwmon_init:
        if (!reload)
                devlink_unregister(devlink);
@@ -1088,6 +1089,7 @@ void mlxsw_core_bus_device_unregister(struct mlxsw_core 
*mlxsw_core,
        if (mlxsw_core->driver->fini)
                mlxsw_core->driver->fini(mlxsw_core);
        mlxsw_thermal_fini(mlxsw_core->thermal);
+       mlxsw_hwmon_fini(mlxsw_core->hwmon);
        if (!reload)
                devlink_unregister(devlink);
        mlxsw_emad_fini(mlxsw_core);
diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.h 
b/drivers/net/ethernet/mellanox/mlxsw/core.h
index 655ddd204ab2..c35be477856f 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/core.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/core.h
@@ -359,6 +359,10 @@ static inline int mlxsw_hwmon_init(struct mlxsw_core 
*mlxsw_core,
        return 0;
 }
 
+static inline void mlxsw_hwmon_fini(struct mlxsw_hwmon *mlxsw_hwmon)
+{
+}
+
 #endif
 
 struct mlxsw_thermal;
diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c 
b/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c
index f6cf2896d337..e04e8162aa14 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c
@@ -303,8 +303,7 @@ int mlxsw_hwmon_init(struct mlxsw_core *mlxsw_core,
        struct device *hwmon_dev;
        int err;
 
-       mlxsw_hwmon = devm_kzalloc(mlxsw_bus_info->dev, sizeof(*mlxsw_hwmon),
-                                  GFP_KERNEL);
+       mlxsw_hwmon = kzalloc(sizeof(*mlxsw_hwmon), GFP_KERNEL);
        if (!mlxsw_hwmon)
                return -ENOMEM;
        mlxsw_hwmon->core = mlxsw_core;
@@ -321,10 +320,9 @@ int mlxsw_hwmon_init(struct mlxsw_core *mlxsw_core,
        mlxsw_hwmon->groups[0] = &mlxsw_hwmon->group;
        mlxsw_hwmon->group.attrs = mlxsw_hwmon->attrs;
 
-       hwmon_dev = devm_hwmon_device_register_with_groups(mlxsw_bus_info->dev,
-                                                          "mlxsw",
-                                                          mlxsw_hwmon,
-                                                          mlxsw_hwmon->groups);
+       hwmon_dev = hwmon_device_register_with_groups(mlxsw_bus_info->dev,
+                                                     "mlxsw", mlxsw_hwmon,
+                                                     mlxsw_hwmon->groups);
        if (IS_ERR(hwmon_dev)) {
                err = PTR_ERR(hwmon_dev);
                goto err_hwmon_register;
@@ -337,5 +335,12 @@ int mlxsw_hwmon_init(struct mlxsw_core *mlxsw_core,
 err_hwmon_register:
 err_fans_init:
 err_temp_init:
+       kfree(mlxsw_hwmon);
        return err;
 }
+
+void mlxsw_hwmon_fini(struct mlxsw_hwmon *mlxsw_hwmon)
+{
+       hwmon_device_unregister(mlxsw_hwmon->hwmon_dev);
+       kfree(mlxsw_hwmon);
+}
-- 
2.17.2

Reply via email to