]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
net/mlx5: Fix slab-out-of-bounds in mlx5_query_nic_vport_mac_list
authorDragos Tatulea <dtatulea@nvidia.com>
Thu, 4 Jun 2026 13:58:49 +0000 (16:58 +0300)
committerJakub Kicinski <kuba@kernel.org>
Tue, 9 Jun 2026 01:56:18 +0000 (18:56 -0700)
mlx5_query_nic_vport_mac_list() sizes its firmware command buffer using
the PF's log_max_current_uc/mc_list capabilities. When querying a VF
vport with a larger configured max (via devlink), the firmware response
can overflow this buffer:

 BUG: KASAN: slab-out-of-bounds in mlx5_query_nic_vport_mac_list+0x453/0x4c0 [mlx5_core]
 Read of size 4 at addr ff1100013ffc8a12 by task kworker/u96:2/385

 CPU: 12 UID: 0 PID: 385 Comm: kworker/u96:2 Not tainted 7.0.0-rc6+ #1 PREEMPT
 Hardware name: QEMU Standard PC (Q35 + ICH9, 2009)
 Workqueue: mlx5_esw_wq esw_vport_change_handler [mlx5_core]
 Call Trace:
  <TASK>
  dump_stack_lvl+0x69/0xa0
  print_report+0x176/0x4e4
  kasan_report+0xc8/0x100
  mlx5_query_nic_vport_mac_list+0x453/0x4c0 [mlx5_core]
  esw_update_vport_addr_list+0x2e3/0xda0 [mlx5_core]
  esw_vport_change_handle_locked+0xa1f/0x1060 [mlx5_core]
  esw_vport_change_handler+0x6a/0x90 [mlx5_core]
  process_one_work+0x87f/0x15e0
  worker_thread+0x62b/0x1020
  kthread+0x375/0x490
  ret_from_fork+0x4dc/0x810
  ret_from_fork_asm+0x11/0x20
  </TASK>

Fix by querying the vport's own HCA caps to size the buffer correctly.
Refactor the function to allocate and return the MAC list internally,
removing the caller's dependency on knowing the correct max.

Fixes: e16aea2744ab ("net/mlx5: Introduce access functions to modify/query vport mac lists")
Signed-off-by: Dragos Tatulea <dtatulea@nvidia.com>
Reviewed-by: Carolina Jubran <cjubran@nvidia.com>
Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
Link: https://patch.msgid.link/20260604135849.458060-1-tariqt@nvidia.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/ethernet/mellanox/mlx5/core/eswitch.c
drivers/net/ethernet/mellanox/mlx5/core/vport.c
include/linux/mlx5/vport.h

index 7c8311f4123237dd221fcdf121b0c5caddb7f73c..236f89a6483af156385827706c7ee252c2d6d510 100644 (file)
@@ -533,23 +533,16 @@ static void esw_update_vport_addr_list(struct mlx5_eswitch *esw,
                                       struct mlx5_vport *vport, int list_type)
 {
        bool is_uc = list_type == MLX5_NVPRT_LIST_TYPE_UC;
-       u8 (*mac_list)[ETH_ALEN];
+       u8 (*mac_list)[ETH_ALEN] = NULL;
        struct l2addr_node *node;
        struct vport_addr *addr;
        struct hlist_head *hash;
        struct hlist_node *tmp;
-       int size;
+       int size = 0;
        int err;
        int hi;
        int i;
 
-       size = is_uc ? MLX5_MAX_UC_PER_VPORT(esw->dev) :
-                      MLX5_MAX_MC_PER_VPORT(esw->dev);
-
-       mac_list = kcalloc(size, ETH_ALEN, GFP_KERNEL);
-       if (!mac_list)
-               return;
-
        hash = is_uc ? vport->uc_list : vport->mc_list;
 
        for_each_l2hash_node(node, tmp, hash, hi) {
@@ -561,7 +554,7 @@ static void esw_update_vport_addr_list(struct mlx5_eswitch *esw,
                goto out;
 
        err = mlx5_query_nic_vport_mac_list(esw->dev, vport->vport, list_type,
-                                           mac_list, &size);
+                                           &mac_list, &size);
        if (err)
                goto out;
        esw_debug(esw->dev, "vport[%d] context update %s list size (%d)\n",
index 4effe37fd4552e0d2e5a38d0b6831f80b5cc4786..d63b0e8806b54424b45504ec8e4af0191fb5ee5a 100644 (file)
@@ -324,35 +324,63 @@ int mlx5_modify_nic_vport_mtu(struct mlx5_core_dev *mdev, u16 mtu)
 }
 EXPORT_SYMBOL_GPL(mlx5_modify_nic_vport_mtu);
 
+static int mlx5_vport_max_mac_list_size(struct mlx5_core_dev *dev, u16 vport,
+                                       enum mlx5_list_type list_type)
+{
+       void *query_ctx, *hca_caps;
+       int ret = 0;
+
+       if (!vport && !mlx5_core_is_ecpf(dev))
+               return list_type == MLX5_NVPRT_LIST_TYPE_UC ?
+                       1 << MLX5_CAP_GEN(dev, log_max_current_uc_list) :
+                       1 << MLX5_CAP_GEN(dev, log_max_current_mc_list);
+
+       query_ctx = kzalloc(MLX5_ST_SZ_BYTES(query_hca_cap_out), GFP_KERNEL);
+       if (!query_ctx)
+               return -ENOMEM;
+
+       ret = mlx5_vport_get_other_func_general_cap(dev, vport, query_ctx);
+       if (ret)
+               goto out;
+
+       hca_caps = MLX5_ADDR_OF(query_hca_cap_out, query_ctx, capability);
+       ret = list_type == MLX5_NVPRT_LIST_TYPE_UC ?
+               1 << MLX5_GET(cmd_hca_cap, hca_caps, log_max_current_uc_list) :
+               1 << MLX5_GET(cmd_hca_cap, hca_caps, log_max_current_mc_list);
+
+out:
+       kfree(query_ctx);
+
+       return ret;
+}
+
 int mlx5_query_nic_vport_mac_list(struct mlx5_core_dev *dev,
                                  u16 vport,
                                  enum mlx5_list_type list_type,
-                                 u8 addr_list[][ETH_ALEN],
-                                 int *list_size)
+                                 u8 (**addr_list)[ETH_ALEN],
+                                 int *addr_list_size)
 {
        u32 in[MLX5_ST_SZ_DW(query_nic_vport_context_in)] = {0};
+       int allowed_list_size;
        void *nic_vport_ctx;
        int max_list_size;
-       int req_list_size;
        int out_sz;
        void *out;
        int err;
        int i;
 
-       req_list_size = *list_size;
+       if (!addr_list || !addr_list_size)
+               return -EINVAL;
 
-       max_list_size = list_type == MLX5_NVPRT_LIST_TYPE_UC ?
-               1 << MLX5_CAP_GEN(dev, log_max_current_uc_list) :
-               1 << MLX5_CAP_GEN(dev, log_max_current_mc_list);
+       *addr_list = NULL;
+       *addr_list_size = 0;
 
-       if (req_list_size > max_list_size) {
-               mlx5_core_warn(dev, "Requested list size (%d) > (%d) max_list_size\n",
-                              req_list_size, max_list_size);
-               req_list_size = max_list_size;
-       }
+       max_list_size = mlx5_vport_max_mac_list_size(dev, vport, list_type);
+       if (max_list_size < 0)
+               return max_list_size;
 
        out_sz = MLX5_ST_SZ_BYTES(query_nic_vport_context_out) +
-                       req_list_size * MLX5_ST_SZ_BYTES(mac_address_layout);
+                       max_list_size * MLX5_ST_SZ_BYTES(mac_address_layout);
 
        out = kvzalloc(out_sz, GFP_KERNEL);
        if (!out)
@@ -371,16 +399,24 @@ int mlx5_query_nic_vport_mac_list(struct mlx5_core_dev *dev,
 
        nic_vport_ctx = MLX5_ADDR_OF(query_nic_vport_context_out, out,
                                     nic_vport_context);
-       req_list_size = MLX5_GET(nic_vport_context, nic_vport_ctx,
-                                allowed_list_size);
+       allowed_list_size = MLX5_GET(nic_vport_context, nic_vport_ctx,
+                                    allowed_list_size);
+       if (!allowed_list_size)
+               goto out;
+
+       *addr_list = kcalloc(allowed_list_size, ETH_ALEN, GFP_KERNEL);
+       if (!*addr_list) {
+               err = -ENOMEM;
+               goto out;
+       }
 
-       *list_size = req_list_size;
-       for (i = 0; i < req_list_size; i++) {
+       for (i = 0; i < allowed_list_size; i++) {
                u8 *mac_addr = MLX5_ADDR_OF(nic_vport_context,
                                        nic_vport_ctx,
                                        current_uc_mac_address[i]) + 2;
-               ether_addr_copy(addr_list[i], mac_addr);
+               ether_addr_copy((*addr_list)[i], mac_addr);
        }
+       *addr_list_size = allowed_list_size;
 out:
        kvfree(out);
        return err;
index dfa2fe32217af0860826539a36b796a0f84f1e03..282ed54422826d512a2d933892cd2add184fe89f 100644 (file)
@@ -102,8 +102,8 @@ int mlx5_query_hca_vport_node_guid(struct mlx5_core_dev *dev,
 int mlx5_query_nic_vport_mac_list(struct mlx5_core_dev *dev,
                                  u16 vport,
                                  enum mlx5_list_type list_type,
-                                 u8 addr_list[][ETH_ALEN],
-                                 int *list_size);
+                                 u8 (**mac_list)[ETH_ALEN],
+                                 int *mac_list_size);
 int mlx5_modify_nic_vport_mac_list(struct mlx5_core_dev *dev,
                                   enum mlx5_list_type list_type,
                                   u8 addr_list[][ETH_ALEN],