[PATCH net V2 2/4] net/mlx5e: ipsec: Block eswitch mode changes before accessing priv->ipsec
From: Tariq Toukan
Date: Wed Sep 30 2026 - 08:19:23 EST
From: Cosmin Ratiu <cratiu@xxxxxxxxxx>
mlx5e_xfrm_add_state() reads priv->ipsec and validates mode-dependent
capabilities before blocking eswitch mode changes. A concurrent profile
change can free the saved IPsec context and cause use-after-free.
Move the mode block before saving the IPsec context and validating the
state, and release it on all error paths. Retain an early availability
check to preserve software fallback when IPsec is unavailable, and check
the context again after taking the mode block. Keep the atomic
acquire-placeholder path exempt, since it creates no hardware state and
cannot take sleeping locks.
As in policy creation, do not check eswitch users when taking this
temporary mode block. This allows states to reuse existing IPsec tables
when TC rules exist on VF representors, instead of rejecting them
unconditionally. New RX/TX tables still check eswitch users, and the
TC/IPsec exclusion counters still reject conflicting packet offloads.
Fixes: 22239eb258bc ("net/mlx5e: Prevent tunnel reformat when tunnel mode not allowed")
Signed-off-by: Cosmin Ratiu <cratiu@xxxxxxxxxx>
Reviewed-by: Dragos Tatulea <dtatulea@xxxxxxxxxx>
Signed-off-by: Tariq Toukan <tariqt@xxxxxxxxxx>
---
.../mellanox/mlx5/core/en_accel/ipsec.c | 50 +++++++++++++------
1 file changed, 34 insertions(+), 16 deletions(-)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c
index 841ecdc2c4d9..cf721ef83d59 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c
@@ -771,28 +771,44 @@ static int mlx5e_xfrm_add_state(struct net_device *dev,
struct xfrm_state *x,
struct netlink_ext_ack *extack)
{
+ bool is_acq = x->xso.flags & XFRM_DEV_OFFLOAD_FLAG_ACQ;
struct mlx5e_ipsec_sa_entry *sa_entry = NULL;
bool allow_tunnel_mode = false;
+ struct mlx5_core_dev *mdev;
struct mlx5e_ipsec *ipsec;
struct mlx5e_priv *priv;
gfp_t gfp;
int err;
priv = netdev_priv(dev);
- if (!priv->ipsec)
+ mdev = priv->mdev;
+ if (!mdev || !priv->ipsec)
return -EOPNOTSUPP;
+ if (!is_acq) {
+ err = mlx5_eswitch_block_mode(mdev, false);
+ if (err)
+ return err;
+ }
+
ipsec = priv->ipsec;
- gfp = (x->xso.flags & XFRM_DEV_OFFLOAD_FLAG_ACQ) ? GFP_ATOMIC : GFP_KERNEL;
+ if (!ipsec) {
+ err = -EOPNOTSUPP;
+ goto unblock_mode;
+ }
+
+ gfp = is_acq ? GFP_ATOMIC : GFP_KERNEL;
sa_entry = kzalloc_obj(*sa_entry, gfp);
- if (!sa_entry)
- return -ENOMEM;
+ if (!sa_entry) {
+ err = -ENOMEM;
+ goto unblock_mode;
+ }
sa_entry->x = x;
sa_entry->dev = dev;
sa_entry->ipsec = ipsec;
/* Check if this SA is originated from acquire flow temporary SA */
- if (x->xso.flags & XFRM_DEV_OFFLOAD_FLAG_ACQ) {
+ if (is_acq) {
x->xso.offload_handle = (unsigned long)sa_entry;
return 0;
}
@@ -806,10 +822,6 @@ static int mlx5e_xfrm_add_state(struct net_device *dev,
goto err_xfrm;
}
- err = mlx5_eswitch_block_mode(priv->mdev, true);
- if (err)
- goto unblock_ipsec;
-
if (x->props.mode == XFRM_MODE_TUNNEL &&
x->xso.type == XFRM_DEV_OFFLOAD_PACKET) {
allow_tunnel_mode = mlx5e_ipsec_fs_tunnel_allowed(sa_entry);
@@ -817,7 +829,7 @@ static int mlx5e_xfrm_add_state(struct net_device *dev,
NL_SET_ERR_MSG_MOD(extack,
"Packet offload tunnel mode is disabled due to encap settings");
err = -EINVAL;
- goto unblock_mode;
+ goto unblock_ipsec;
}
}
@@ -876,7 +888,7 @@ static int mlx5e_xfrm_add_state(struct net_device *dev,
if (allow_tunnel_mode)
mlx5_eswitch_unblock_encap(priv->mdev);
- mlx5_eswitch_unblock_mode(priv->mdev);
+ mlx5_eswitch_unblock_mode(mdev);
return 0;
@@ -893,13 +905,14 @@ static int mlx5e_xfrm_add_state(struct net_device *dev,
unblock_encap:
if (allow_tunnel_mode)
mlx5_eswitch_unblock_encap(priv->mdev);
-unblock_mode:
- mlx5_eswitch_unblock_mode(priv->mdev);
unblock_ipsec:
mlx5_eswitch_unblock_ipsec(priv->mdev);
err_xfrm:
kfree(sa_entry);
NL_SET_ERR_MSG_WEAK_MOD(extack, "Device failed to offload this state");
+unblock_mode:
+ if (!is_acq)
+ mlx5_eswitch_unblock_mode(mdev);
return err;
}
@@ -1262,12 +1275,17 @@ static int mlx5e_xfrm_add_policy(struct xfrm_policy *x,
{
struct net_device *netdev = x->xdo.dev;
struct mlx5e_ipsec_pol_entry *pol_entry;
+ struct mlx5_core_dev *mdev;
struct mlx5e_priv *priv;
int err;
priv = netdev_priv(netdev);
+ mdev = priv->mdev;
+ if (!mdev)
+ return -EOPNOTSUPP;
+
/* Block esw mode changes until the policy holds its own block. */
- err = mlx5_eswitch_block_mode(priv->mdev, false);
+ err = mlx5_eswitch_block_mode(mdev, false);
if (err) {
NL_SET_ERR_MSG_MOD(extack, "Eswitch busy, can't add policy");
return err;
@@ -1303,7 +1321,7 @@ static int mlx5e_xfrm_add_policy(struct xfrm_policy *x,
goto err_fs;
x->xdo.offload_handle = (unsigned long)pol_entry;
- mlx5_eswitch_unblock_mode(priv->mdev);
+ mlx5_eswitch_unblock_mode(mdev);
return 0;
err_fs:
@@ -1312,7 +1330,7 @@ static int mlx5e_xfrm_add_policy(struct xfrm_policy *x,
kfree(pol_entry);
NL_SET_ERR_MSG_MOD(extack, "Device failed to offload this policy");
unblock_mode:
- mlx5_eswitch_unblock_mode(priv->mdev);
+ mlx5_eswitch_unblock_mode(mdev);
return err;
}
--
2.44.0