Commit 9b3bc7db authored by Ido Schimmel's avatar Ido Schimmel Committed by David S. Miller

mlxsw: core: Fix use-after-free when flashing firmware during init

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: c86d62cc ("mlxsw: spectrum: Reset FW after flash")
Signed-off-by: default avatarIdo Schimmel <idosch@mellanox.com>
Reported-by: default avatarAlexander Petrovskiy <alexpe@mellanox.com>
Tested-by: default avatarAlexander Petrovskiy <alexpe@mellanox.com>
Reviewed-by: default avatarPetr Machata <petrm@mellanox.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent c863850c
...@@ -1055,6 +1055,7 @@ int mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info, ...@@ -1055,6 +1055,7 @@ int mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info,
err_driver_init: err_driver_init:
mlxsw_thermal_fini(mlxsw_core->thermal); mlxsw_thermal_fini(mlxsw_core->thermal);
err_thermal_init: err_thermal_init:
mlxsw_hwmon_fini(mlxsw_core->hwmon);
err_hwmon_init: err_hwmon_init:
if (!reload) if (!reload)
devlink_unregister(devlink); devlink_unregister(devlink);
...@@ -1088,6 +1089,7 @@ void mlxsw_core_bus_device_unregister(struct mlxsw_core *mlxsw_core, ...@@ -1088,6 +1089,7 @@ void mlxsw_core_bus_device_unregister(struct mlxsw_core *mlxsw_core,
if (mlxsw_core->driver->fini) if (mlxsw_core->driver->fini)
mlxsw_core->driver->fini(mlxsw_core); mlxsw_core->driver->fini(mlxsw_core);
mlxsw_thermal_fini(mlxsw_core->thermal); mlxsw_thermal_fini(mlxsw_core->thermal);
mlxsw_hwmon_fini(mlxsw_core->hwmon);
if (!reload) if (!reload)
devlink_unregister(devlink); devlink_unregister(devlink);
mlxsw_emad_fini(mlxsw_core); mlxsw_emad_fini(mlxsw_core);
......
...@@ -359,6 +359,10 @@ static inline int mlxsw_hwmon_init(struct mlxsw_core *mlxsw_core, ...@@ -359,6 +359,10 @@ static inline int mlxsw_hwmon_init(struct mlxsw_core *mlxsw_core,
return 0; return 0;
} }
static inline void mlxsw_hwmon_fini(struct mlxsw_hwmon *mlxsw_hwmon)
{
}
#endif #endif
struct mlxsw_thermal; struct mlxsw_thermal;
......
...@@ -303,8 +303,7 @@ int mlxsw_hwmon_init(struct mlxsw_core *mlxsw_core, ...@@ -303,8 +303,7 @@ int mlxsw_hwmon_init(struct mlxsw_core *mlxsw_core,
struct device *hwmon_dev; struct device *hwmon_dev;
int err; int err;
mlxsw_hwmon = devm_kzalloc(mlxsw_bus_info->dev, sizeof(*mlxsw_hwmon), mlxsw_hwmon = kzalloc(sizeof(*mlxsw_hwmon), GFP_KERNEL);
GFP_KERNEL);
if (!mlxsw_hwmon) if (!mlxsw_hwmon)
return -ENOMEM; return -ENOMEM;
mlxsw_hwmon->core = mlxsw_core; mlxsw_hwmon->core = mlxsw_core;
...@@ -321,10 +320,9 @@ int mlxsw_hwmon_init(struct mlxsw_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->groups[0] = &mlxsw_hwmon->group;
mlxsw_hwmon->group.attrs = mlxsw_hwmon->attrs; mlxsw_hwmon->group.attrs = mlxsw_hwmon->attrs;
hwmon_dev = devm_hwmon_device_register_with_groups(mlxsw_bus_info->dev, hwmon_dev = hwmon_device_register_with_groups(mlxsw_bus_info->dev,
"mlxsw", "mlxsw", mlxsw_hwmon,
mlxsw_hwmon, mlxsw_hwmon->groups);
mlxsw_hwmon->groups);
if (IS_ERR(hwmon_dev)) { if (IS_ERR(hwmon_dev)) {
err = PTR_ERR(hwmon_dev); err = PTR_ERR(hwmon_dev);
goto err_hwmon_register; goto err_hwmon_register;
...@@ -337,5 +335,12 @@ int mlxsw_hwmon_init(struct mlxsw_core *mlxsw_core, ...@@ -337,5 +335,12 @@ int mlxsw_hwmon_init(struct mlxsw_core *mlxsw_core,
err_hwmon_register: err_hwmon_register:
err_fans_init: err_fans_init:
err_temp_init: err_temp_init:
kfree(mlxsw_hwmon);
return err; return err;
} }
void mlxsw_hwmon_fini(struct mlxsw_hwmon *mlxsw_hwmon)
{
hwmon_device_unregister(mlxsw_hwmon->hwmon_dev);
kfree(mlxsw_hwmon);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment