]> git.baikalelectronics.ru Git - kernel.git/commitdiff
mlxsw: spectrum: Fix use-after-free of split/unsplit/type_set in case reload fails
authorJiri Pirko <jiri@mellanox.com>
Thu, 21 May 2020 12:11:44 +0000 (15:11 +0300)
committerDavid S. Miller <davem@davemloft.net>
Fri, 22 May 2020 23:08:14 +0000 (16:08 -0700)
In case of reload fail, the mlxsw_sp->ports contains a pointer to a
freed memory (either by reload_down() or reload_up() error path).
Fix this by initializing the pointer to NULL and checking it before
dereferencing in split/unsplit/type_set callpaths.

Fixes: 965f7a2a41d2 ("mlxsw: core: Add support for reload")
Reported-by: Danielle Ratson <danieller@mellanox.com>
Signed-off-by: Jiri Pirko <jiri@mellanox.com>
Signed-off-by: Ido Schimmel <idosch@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/mellanox/mlxsw/spectrum.c
drivers/net/ethernet/mellanox/mlxsw/switchx2.c

index 24ca8d5bc56410dfa63833ceefb00ffd4d8a4926..6b39978acd0781dde5c37ca110b3b0c595eed49a 100644 (file)
@@ -3986,6 +3986,7 @@ static void mlxsw_sp_ports_remove(struct mlxsw_sp *mlxsw_sp)
                        mlxsw_sp_port_remove(mlxsw_sp, i);
        mlxsw_sp_cpu_port_remove(mlxsw_sp);
        kfree(mlxsw_sp->ports);
+       mlxsw_sp->ports = NULL;
 }
 
 static int mlxsw_sp_ports_create(struct mlxsw_sp *mlxsw_sp)
@@ -4022,6 +4023,7 @@ err_port_create:
        mlxsw_sp_cpu_port_remove(mlxsw_sp);
 err_cpu_port_create:
        kfree(mlxsw_sp->ports);
+       mlxsw_sp->ports = NULL;
        return err;
 }
 
@@ -4143,6 +4145,14 @@ static int mlxsw_sp_local_ports_offset(struct mlxsw_core *mlxsw_core,
        return mlxsw_core_res_get(mlxsw_core, local_ports_in_x_res_id);
 }
 
+static struct mlxsw_sp_port *
+mlxsw_sp_port_get_by_local_port(struct mlxsw_sp *mlxsw_sp, u8 local_port)
+{
+       if (mlxsw_sp->ports && mlxsw_sp->ports[local_port])
+               return mlxsw_sp->ports[local_port];
+       return NULL;
+}
+
 static int mlxsw_sp_port_split(struct mlxsw_core *mlxsw_core, u8 local_port,
                               unsigned int count,
                               struct netlink_ext_ack *extack)
@@ -4156,7 +4166,7 @@ static int mlxsw_sp_port_split(struct mlxsw_core *mlxsw_core, u8 local_port,
        int i;
        int err;
 
-       mlxsw_sp_port = mlxsw_sp->ports[local_port];
+       mlxsw_sp_port = mlxsw_sp_port_get_by_local_port(mlxsw_sp, local_port);
        if (!mlxsw_sp_port) {
                dev_err(mlxsw_sp->bus_info->dev, "Port number \"%d\" does not exist\n",
                        local_port);
@@ -4251,7 +4261,7 @@ static int mlxsw_sp_port_unsplit(struct mlxsw_core *mlxsw_core, u8 local_port,
        int offset;
        int i;
 
-       mlxsw_sp_port = mlxsw_sp->ports[local_port];
+       mlxsw_sp_port = mlxsw_sp_port_get_by_local_port(mlxsw_sp, local_port);
        if (!mlxsw_sp_port) {
                dev_err(mlxsw_sp->bus_info->dev, "Port number \"%d\" does not exist\n",
                        local_port);
index 90535820b559db36040c7758657ad8d3758a7267..2503f61db5fbe284a4b89c4aa2bf6e1849727e70 100644 (file)
@@ -1259,6 +1259,7 @@ static void mlxsw_sx_ports_remove(struct mlxsw_sx *mlxsw_sx)
                if (mlxsw_sx_port_created(mlxsw_sx, i))
                        mlxsw_sx_port_remove(mlxsw_sx, i);
        kfree(mlxsw_sx->ports);
+       mlxsw_sx->ports = NULL;
 }
 
 static int mlxsw_sx_ports_create(struct mlxsw_sx *mlxsw_sx)
@@ -1293,6 +1294,7 @@ err_port_module_info_get:
                if (mlxsw_sx_port_created(mlxsw_sx, i))
                        mlxsw_sx_port_remove(mlxsw_sx, i);
        kfree(mlxsw_sx->ports);
+       mlxsw_sx->ports = NULL;
        return err;
 }
 
@@ -1376,6 +1378,12 @@ static int mlxsw_sx_port_type_set(struct mlxsw_core *mlxsw_core, u8 local_port,
        u8 module, width;
        int err;
 
+       if (!mlxsw_sx->ports || !mlxsw_sx->ports[local_port]) {
+               dev_err(mlxsw_sx->bus_info->dev, "Port number \"%d\" does not exist\n",
+                       local_port);
+               return -EINVAL;
+       }
+
        if (new_type == DEVLINK_PORT_TYPE_AUTO)
                return -EOPNOTSUPP;