Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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);
+4 −0
Original line number Diff line number Diff line
@@ -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;
+11 −6
Original line number Diff line number Diff line
@@ -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,9 +320,8 @@ 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,
	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);
@@ -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);
}