net/mlx5: fs, fix UAF in flow counter release
authorMoshe Shemesh <moshe@nvidia.com>
Mon, 22 Sep 2025 07:11:32 +0000 (10:11 +0300)
committerJakub Kicinski <kuba@kernel.org>
Wed, 24 Sep 2025 00:17:30 +0000 (17:17 -0700)
Fix a kernel trace [1] caused by releasing an HWS action of a local flow
counter in mlx5_cmd_hws_delete_fte(), where the HWS action refcount and
mutex were not initialized and the counter struct could already be freed
when deleting the rule.

Fix it by adding the missing initializations and adding refcount for the
local flow counter struct.

[1] Kernel log:
 Call Trace:
  <TASK>
  dump_stack_lvl+0x34/0x48
  mlx5_fs_put_hws_action.part.0.cold+0x21/0x94 [mlx5_core]
  mlx5_fc_put_hws_action+0x96/0xad [mlx5_core]
  mlx5_fs_destroy_fs_actions+0x8b/0x152 [mlx5_core]
  mlx5_cmd_hws_delete_fte+0x5a/0xa0 [mlx5_core]
  del_hw_fte+0x1ce/0x260 [mlx5_core]
  mlx5_del_flow_rules+0x12d/0x240 [mlx5_core]
  ? ttwu_queue_wakelist+0xf4/0x110
  mlx5_ib_destroy_flow+0x103/0x1b0 [mlx5_ib]
  uverbs_free_flow+0x20/0x50 [ib_uverbs]
  destroy_hw_idr_uobject+0x1b/0x50 [ib_uverbs]
  uverbs_destroy_uobject+0x34/0x1a0 [ib_uverbs]
  uobj_destroy+0x3c/0x80 [ib_uverbs]
  ib_uverbs_run_method+0x23e/0x360 [ib_uverbs]
  ? uverbs_finalize_object+0x60/0x60 [ib_uverbs]
  ib_uverbs_cmd_verbs+0x14f/0x2c0 [ib_uverbs]
  ? do_tty_write+0x1a9/0x270
  ? file_tty_write.constprop.0+0x98/0xc0
  ? new_sync_write+0xfc/0x190
  ib_uverbs_ioctl+0xd7/0x160 [ib_uverbs]
  __x64_sys_ioctl+0x87/0xc0
  do_syscall_64+0x59/0x90

Fixes: b581f4266928 ("net/mlx5: fs, manage flow counters HWS action sharing by refcount")
Signed-off-by: Moshe Shemesh <moshe@nvidia.com>
Reviewed-by: Yevgeny Kliteynik <kliteyn@nvidia.com>
Reviewed-by: Mark Bloch <mbloch@nvidia.com>
Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
Link: https://patch.msgid.link/1758525094-816583-2-git-send-email-tariqt@nvidia.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/ethernet/mellanox/mlx5/core/fs_core.c
drivers/net/ethernet/mellanox/mlx5/core/fs_core.h
drivers/net/ethernet/mellanox/mlx5/core/fs_counters.c
drivers/net/ethernet/mellanox/mlx5/core/steering/hws/fs_hws_pools.c
include/linux/mlx5/fs.h

index db552c0..80245c3 100644 (file)
@@ -663,7 +663,7 @@ static void del_sw_hw_rule(struct fs_node *node)
                        BIT(MLX5_SET_FTE_MODIFY_ENABLE_MASK_ACTION) |
                        BIT(MLX5_SET_FTE_MODIFY_ENABLE_MASK_FLOW_COUNTERS);
                fte->act_dests.action.action &= ~MLX5_FLOW_CONTEXT_ACTION_COUNT;
-               mlx5_fc_local_destroy(rule->dest_attr.counter);
+               mlx5_fc_local_put(rule->dest_attr.counter);
                goto out;
        }
 
index 5008262..e6a95b3 100644 (file)
@@ -343,6 +343,7 @@ struct mlx5_fc {
        enum mlx5_fc_type type;
        struct mlx5_fc_bulk *bulk;
        struct mlx5_fc_cache cache;
+       refcount_t fc_local_refcount;
        /* last{packets,bytes} are used for calculating deltas since last reading. */
        u64 lastpackets;
        u64 lastbytes;
index 492775d..83001ed 100644 (file)
@@ -562,17 +562,36 @@ mlx5_fc_local_create(u32 counter_id, u32 offset, u32 bulk_size)
        counter->id = counter_id;
        fc_bulk->base_id = counter_id - offset;
        fc_bulk->fs_bulk.bulk_len = bulk_size;
+       refcount_set(&fc_bulk->hws_data.hws_action_refcount, 0);
+       mutex_init(&fc_bulk->hws_data.lock);
        counter->bulk = fc_bulk;
+       refcount_set(&counter->fc_local_refcount, 1);
        return counter;
 }
 EXPORT_SYMBOL(mlx5_fc_local_create);
 
 void mlx5_fc_local_destroy(struct mlx5_fc *counter)
 {
-       if (!counter || counter->type != MLX5_FC_TYPE_LOCAL)
-               return;
-
        kfree(counter->bulk);
        kfree(counter);
 }
 EXPORT_SYMBOL(mlx5_fc_local_destroy);
+
+void mlx5_fc_local_get(struct mlx5_fc *counter)
+{
+       if (!counter || counter->type != MLX5_FC_TYPE_LOCAL)
+               return;
+
+       refcount_inc(&counter->fc_local_refcount);
+}
+
+void mlx5_fc_local_put(struct mlx5_fc *counter)
+{
+       if (!counter || counter->type != MLX5_FC_TYPE_LOCAL)
+               return;
+
+       if (!refcount_dec_and_test(&counter->fc_local_refcount))
+               return;
+
+       mlx5_fc_local_destroy(counter);
+}
index f1ecdba..839d71b 100644 (file)
@@ -407,15 +407,21 @@ struct mlx5hws_action *mlx5_fc_get_hws_action(struct mlx5hws_context *ctx,
 {
        struct mlx5_fs_hws_create_action_ctx create_ctx;
        struct mlx5_fc_bulk *fc_bulk = counter->bulk;
+       struct mlx5hws_action *hws_action;
 
        create_ctx.hws_ctx = ctx;
        create_ctx.id = fc_bulk->base_id;
        create_ctx.actions_type = MLX5HWS_ACTION_TYP_CTR;
 
-       return mlx5_fs_get_hws_action(&fc_bulk->hws_data, &create_ctx);
+       mlx5_fc_local_get(counter);
+       hws_action = mlx5_fs_get_hws_action(&fc_bulk->hws_data, &create_ctx);
+       if (!hws_action)
+               mlx5_fc_local_put(counter);
+       return hws_action;
 }
 
 void mlx5_fc_put_hws_action(struct mlx5_fc *counter)
 {
        mlx5_fs_put_hws_action(&counter->bulk->hws_data);
+       mlx5_fc_local_put(counter);
 }
index 86055d5..6ac76a0 100644 (file)
@@ -308,6 +308,8 @@ struct mlx5_fc *mlx5_fc_create(struct mlx5_core_dev *dev, bool aging);
 void mlx5_fc_destroy(struct mlx5_core_dev *dev, struct mlx5_fc *counter);
 struct mlx5_fc *mlx5_fc_local_create(u32 counter_id, u32 offset, u32 bulk_size);
 void mlx5_fc_local_destroy(struct mlx5_fc *counter);
+void mlx5_fc_local_get(struct mlx5_fc *counter);
+void mlx5_fc_local_put(struct mlx5_fc *counter);
 u64 mlx5_fc_query_lastuse(struct mlx5_fc *counter);
 void mlx5_fc_query_cached(struct mlx5_fc *counter,
                          u64 *bytes, u64 *packets, u64 *lastuse);