* [PATCH v4 net 0/3] i40e: re-init and UAF fixes
@ 2026-06-25 15:14 Maciej Fijalkowski
2026-06-25 15:14 ` [PATCH v4 net 1/3] i40e: unregister netdev before clearing VSI on reinit failure Maciej Fijalkowski
` (2 more replies)
0 siblings, 3 replies; 7+ messages in thread
From: Maciej Fijalkowski @ 2026-06-25 15:14 UTC (permalink / raw)
To: intel-wired-lan
Cc: netdev, magnus.karlsson, kuba, pabeni, horms, przemyslaw.kitszel,
jacob.e.keller, Maciej Fijalkowski
v4:
- add preceding patch that fixes a case when some of re-init allocations
failed and we missed de-registering netdev at failure path
- pull out i40e_vsi_setup() changes onto separate patch
v3:
- address UAF when ring arrays were freed before q_vector's ring
containers (Sashiko, Jacob)
- remove bool params from alloc/free array routines (Simon)
v2:
- NULL vsi->tx_rings in i40e_vsi_alloc_arrays() (Sashiko)
Maciej Fijalkowski (3):
i40e: unregister netdev before clearing VSI on reinit failure
i40e: fix potential UAF in i40e_vsi_setup()'s error path
i40e: keep q_vectors array in sync with channel count changes
drivers/net/ethernet/intel/i40e/i40e_main.c | 76 ++++++++++++---------
1 file changed, 45 insertions(+), 31 deletions(-)
--
2.43.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v4 net 1/3] i40e: unregister netdev before clearing VSI on reinit failure
2026-06-25 15:14 [PATCH v4 net 0/3] i40e: re-init and UAF fixes Maciej Fijalkowski
@ 2026-06-25 15:14 ` Maciej Fijalkowski
2026-06-26 6:45 ` [Intel-wired-lan] " Loktionov, Aleksandr
2026-06-30 11:11 ` Maciej Fijalkowski
2026-06-25 15:14 ` [PATCH v4 net 2/3] i40e: fix potential UAF in i40e_vsi_setup()'s error path Maciej Fijalkowski
2026-06-25 15:14 ` [PATCH v4 net 3/3] i40e: keep q_vectors array in sync with channel count changes Maciej Fijalkowski
2 siblings, 2 replies; 7+ messages in thread
From: Maciej Fijalkowski @ 2026-06-25 15:14 UTC (permalink / raw)
To: intel-wired-lan
Cc: netdev, magnus.karlsson, kuba, pabeni, horms, przemyslaw.kitszel,
jacob.e.keller, Maciej Fijalkowski
i40e_vsi_reinit_setup() tears down the existing VSI queue/ring backing
state before allocating replacement arrays and queue tracking. If one of
these early allocations fails, the function jumps directly to err_vsi
and calls i40e_vsi_clear().
For a registered netdev, this frees the VSI while
netdev_priv(netdev)->vsi can still point at it, leaving the registered
netdev with dangling private driver state.
Split the error path so failures after destructive reinit teardown first
unregister and free the netdev before clearing the VSI.
Fixes: d2a69fefd756 ("i40e: Fix changing previously set num_queue_pairs for PFs")
Signed-off-by: Maciej Fijalkowski <maciej.fijalkowski@intel.com>
---
drivers/net/ethernet/intel/i40e/i40e_main.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index a04683004a56..471fa7f7b643 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -14274,7 +14274,7 @@ static struct i40e_vsi *i40e_vsi_reinit_setup(struct i40e_vsi *vsi)
i40e_set_num_rings_in_vsi(vsi);
ret = i40e_vsi_alloc_arrays(vsi, false);
if (ret)
- goto err_vsi;
+ goto err_netdev;
alloc_queue_pairs = vsi->alloc_queue_pairs *
(i40e_enabled_xdp_vsi(vsi) ? 2 : 1);
@@ -14284,7 +14284,7 @@ static struct i40e_vsi *i40e_vsi_reinit_setup(struct i40e_vsi *vsi)
dev_info(&pf->pdev->dev,
"failed to get tracking for %d queues for VSI %d err %d\n",
alloc_queue_pairs, vsi->seid, ret);
- goto err_vsi;
+ goto err_netdev;
}
vsi->base_queue = ret;
@@ -14309,6 +14309,7 @@ static struct i40e_vsi *i40e_vsi_reinit_setup(struct i40e_vsi *vsi)
err_rings:
i40e_vsi_free_q_vectors(vsi);
+err_netdev:
if (vsi->netdev_registered) {
vsi->netdev_registered = false;
unregister_netdev(vsi->netdev);
@@ -14318,7 +14319,6 @@ static struct i40e_vsi *i40e_vsi_reinit_setup(struct i40e_vsi *vsi)
if (vsi->type == I40E_VSI_MAIN)
i40e_devlink_destroy_port(pf);
i40e_aq_delete_element(&pf->hw, vsi->seid, NULL);
-err_vsi:
i40e_vsi_clear(vsi);
return NULL;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v4 net 2/3] i40e: fix potential UAF in i40e_vsi_setup()'s error path
2026-06-25 15:14 [PATCH v4 net 0/3] i40e: re-init and UAF fixes Maciej Fijalkowski
2026-06-25 15:14 ` [PATCH v4 net 1/3] i40e: unregister netdev before clearing VSI on reinit failure Maciej Fijalkowski
@ 2026-06-25 15:14 ` Maciej Fijalkowski
2026-06-26 6:45 ` [Intel-wired-lan] " Loktionov, Aleksandr
2026-06-25 15:14 ` [PATCH v4 net 3/3] i40e: keep q_vectors array in sync with channel count changes Maciej Fijalkowski
2 siblings, 1 reply; 7+ messages in thread
From: Maciej Fijalkowski @ 2026-06-25 15:14 UTC (permalink / raw)
To: intel-wired-lan
Cc: netdev, magnus.karlsson, kuba, pabeni, horms, przemyslaw.kitszel,
jacob.e.keller, Maciej Fijalkowski
Sashiko pointed out an issue where error path in i40e_vsi_reinit_setup()
released ring memory but then when freeing q_vectors, the rings mapped
to q_vectors where touched which implies a regular use-after-free bug.
Apparently i40e_vsi_setup() has the same problem, so swap the allocation
and freeing order and fix the 13 year old bug.
Fixes: 41c445ff0f48 ("i40e: main driver core")
Signed-off-by: Maciej Fijalkowski <maciej.fijalkowski@intel.com>
---
drivers/net/ethernet/intel/i40e/i40e_main.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index 471fa7f7b643..4adc7b0fb2f4 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -14460,14 +14460,14 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type,
fallthrough;
case I40E_VSI_FDIR:
/* set up vectors and rings if needed */
- ret = i40e_vsi_setup_vectors(vsi);
- if (ret)
- goto err_msix;
-
ret = i40e_alloc_rings(vsi);
if (ret)
goto err_rings;
+ ret = i40e_vsi_setup_vectors(vsi);
+ if (ret)
+ goto err_qvec;
+
/* map all of the rings to the q_vectors */
i40e_vsi_map_rings_to_vectors(vsi);
@@ -14487,10 +14487,10 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type,
return vsi;
err_config:
+ i40e_vsi_free_q_vectors(vsi);
+err_qvec:
i40e_vsi_clear_rings(vsi);
err_rings:
- i40e_vsi_free_q_vectors(vsi);
-err_msix:
if (vsi->netdev_registered) {
vsi->netdev_registered = false;
unregister_netdev(vsi->netdev);
--
2.43.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v4 net 3/3] i40e: keep q_vectors array in sync with channel count changes
2026-06-25 15:14 [PATCH v4 net 0/3] i40e: re-init and UAF fixes Maciej Fijalkowski
2026-06-25 15:14 ` [PATCH v4 net 1/3] i40e: unregister netdev before clearing VSI on reinit failure Maciej Fijalkowski
2026-06-25 15:14 ` [PATCH v4 net 2/3] i40e: fix potential UAF in i40e_vsi_setup()'s error path Maciej Fijalkowski
@ 2026-06-25 15:14 ` Maciej Fijalkowski
2 siblings, 0 replies; 7+ messages in thread
From: Maciej Fijalkowski @ 2026-06-25 15:14 UTC (permalink / raw)
To: intel-wired-lan
Cc: netdev, magnus.karlsson, kuba, pabeni, horms, przemyslaw.kitszel,
jacob.e.keller, Maciej Fijalkowski
For the main VSI, i40e_set_num_rings_in_vsi() always derives
num_q_vectors from pf->num_lan_msix. At the same time, ethtool -L stores
the user requested channel count in vsi->req_queue_pairs and the queue
setup path uses that value for the effective number of queue pairs.
This leaves queue and vector counts out of sync after shrinking channel
count via ethtool -L. The active queue configuration is reduced, but the
VSI still keeps the full PF-sized q_vector topology.
That mismatch breaks reconfiguration flows which rely on vector/NAPI
state matching the effective channel configuration. In particular,
toggling /sys/class/net/<dev>/threaded after reducing the channel count
can hang, and later channel-count changes can fail because VSI reinit
does not rebuild q_vectors to match the new vector count.
Fix this by making the main VSI num_q_vectors follow the effective
requested channel count, capped by the available MSI-X vectors. Update
i40e_vsi_reinit_setup() to rebuild q_vectors during VSI reinit so the
vector topology is refreshed together with the ring arrays when channel
count changes.
Keep alloc_queue_pairs unchanged and based on pf->num_lan_qps so the VSI
retains its full queue capacity.
Selftest napi_threaded.py was originally used when Jakub reported hang
on /sys/class/net/<dev>/threaded toggle. In order to make it pass on
i40e, use persistent NAPI configuration for q_vector NAPIs so NAPI
identity and threaded settings survive q_vector reallocation across
channel-count changes. This is achieved by using netif_napi_add_config()
when configuring q_vectors.
$ export NETIF=ens259f1np1
$ sudo -E env PATH="$PATH" ./tools/testing/selftests/drivers/net/napi_threaded.py
TAP version 13
1..3
ok 1 napi_threaded.napi_init
ok 2 napi_threaded.change_num_queues
ok 3 napi_threaded.enable_dev_threaded_disable_napi_threaded
Totals: pass:3 fail:0 xfail:0 xpass:0 skip:0 error:0
Reported-by: Jakub Kicinski <kuba@kernel.org>
Closes: https://lore.kernel.org/intel-wired-lan/20260316133100.6054a11f@kernel.org/
Fixes: d2a69fefd756 ("i40e: Fix changing previously set num_queue_pairs for PFs")
Signed-off-by: Maciej Fijalkowski <maciej.fijalkowski@intel.com>
---
drivers/net/ethernet/intel/i40e/i40e_main.c | 60 +++++++++++++--------
1 file changed, 37 insertions(+), 23 deletions(-)
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index 4adc7b0fb2f4..c017217a1bc3 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -11406,10 +11406,14 @@ static void i40e_service_timer(struct timer_list *t)
static int i40e_set_num_rings_in_vsi(struct i40e_vsi *vsi)
{
struct i40e_pf *pf = vsi->back;
+ u16 qps;
switch (vsi->type) {
case I40E_VSI_MAIN:
vsi->alloc_queue_pairs = pf->num_lan_qps;
+ qps = vsi->req_queue_pairs ?
+ min(vsi->req_queue_pairs, pf->num_lan_qps) :
+ pf->num_lan_qps;
if (!vsi->num_tx_desc)
vsi->num_tx_desc = ALIGN(I40E_DEFAULT_NUM_DESCRIPTORS,
I40E_REQ_DESCRIPTOR_MULTIPLE);
@@ -11417,7 +11421,7 @@ static int i40e_set_num_rings_in_vsi(struct i40e_vsi *vsi)
vsi->num_rx_desc = ALIGN(I40E_DEFAULT_NUM_DESCRIPTORS,
I40E_REQ_DESCRIPTOR_MULTIPLE);
if (test_bit(I40E_FLAG_MSIX_ENA, pf->flags))
- vsi->num_q_vectors = pf->num_lan_msix;
+ vsi->num_q_vectors = clamp(qps, 1, pf->num_lan_msix);
else
vsi->num_q_vectors = 1;
@@ -11469,12 +11473,11 @@ static int i40e_set_num_rings_in_vsi(struct i40e_vsi *vsi)
/**
* i40e_vsi_alloc_arrays - Allocate queue and vector pointer arrays for the vsi
* @vsi: VSI pointer
- * @alloc_qvectors: a bool to specify if q_vectors need to be allocated.
*
* On error: returns error code (negative)
* On success: returns 0
**/
-static int i40e_vsi_alloc_arrays(struct i40e_vsi *vsi, bool alloc_qvectors)
+static int i40e_vsi_alloc_arrays(struct i40e_vsi *vsi)
{
struct i40e_ring **next_rings;
int size;
@@ -11493,19 +11496,18 @@ static int i40e_vsi_alloc_arrays(struct i40e_vsi *vsi, bool alloc_qvectors)
}
vsi->rx_rings = next_rings;
- if (alloc_qvectors) {
- /* allocate memory for q_vector pointers */
- size = sizeof(struct i40e_q_vector *) * vsi->num_q_vectors;
- vsi->q_vectors = kzalloc(size, GFP_KERNEL);
- if (!vsi->q_vectors) {
- ret = -ENOMEM;
- goto err_vectors;
- }
+ /* allocate memory for q_vector pointers */
+ size = sizeof(struct i40e_q_vector *) * vsi->num_q_vectors;
+ vsi->q_vectors = kzalloc(size, GFP_KERNEL);
+ if (!vsi->q_vectors) {
+ ret = -ENOMEM;
+ goto err_vectors;
}
return ret;
err_vectors:
kfree(vsi->tx_rings);
+ vsi->tx_rings = NULL;
return ret;
}
@@ -11578,7 +11580,7 @@ static int i40e_vsi_mem_alloc(struct i40e_pf *pf, enum i40e_vsi_type type)
if (ret)
goto err_rings;
- ret = i40e_vsi_alloc_arrays(vsi, true);
+ ret = i40e_vsi_alloc_arrays(vsi);
if (ret)
goto err_rings;
@@ -11603,18 +11605,15 @@ static int i40e_vsi_mem_alloc(struct i40e_pf *pf, enum i40e_vsi_type type)
/**
* i40e_vsi_free_arrays - Free queue and vector pointer arrays for the VSI
* @vsi: VSI pointer
- * @free_qvectors: a bool to specify if q_vectors need to be freed.
*
* On error: returns error code (negative)
* On success: returns 0
**/
-static void i40e_vsi_free_arrays(struct i40e_vsi *vsi, bool free_qvectors)
+static void i40e_vsi_free_arrays(struct i40e_vsi *vsi)
{
/* free the ring and vector containers */
- if (free_qvectors) {
- kfree(vsi->q_vectors);
- vsi->q_vectors = NULL;
- }
+ kfree(vsi->q_vectors);
+ vsi->q_vectors = NULL;
kfree(vsi->tx_rings);
vsi->tx_rings = NULL;
vsi->rx_rings = NULL;
@@ -11674,7 +11673,7 @@ static int i40e_vsi_clear(struct i40e_vsi *vsi)
i40e_put_lump(pf->irq_pile, vsi->base_vector, vsi->idx);
bitmap_free(vsi->af_xdp_zc_qps);
- i40e_vsi_free_arrays(vsi, true);
+ i40e_vsi_free_arrays(vsi);
i40e_clear_rss_config_user(vsi);
pf->vsi[vsi->idx] = NULL;
@@ -12046,7 +12045,8 @@ static int i40e_vsi_alloc_q_vector(struct i40e_vsi *vsi, int v_idx)
cpumask_copy(&q_vector->affinity_mask, cpu_possible_mask);
if (vsi->netdev)
- netif_napi_add(vsi->netdev, &q_vector->napi, i40e_napi_poll);
+ netif_napi_add_config(vsi->netdev, &q_vector->napi,
+ i40e_napi_poll, v_idx);
/* tie q_vector and vsi together */
vsi->q_vectors[v_idx] = q_vector;
@@ -14267,12 +14267,26 @@ static struct i40e_vsi *i40e_vsi_reinit_setup(struct i40e_vsi *vsi)
pf = vsi->back;
+ if (test_bit(I40E_FLAG_MSIX_ENA, pf->flags)) {
+ i40e_put_lump(pf->irq_pile, vsi->base_vector, vsi->idx);
+ vsi->base_vector = 0;
+ }
+
i40e_put_lump(pf->qp_pile, vsi->base_queue, vsi->idx);
+ i40e_vsi_free_q_vectors(vsi);
i40e_vsi_clear_rings(vsi);
+ i40e_vsi_free_arrays(vsi);
- i40e_vsi_free_arrays(vsi, false);
i40e_set_num_rings_in_vsi(vsi);
- ret = i40e_vsi_alloc_arrays(vsi, false);
+ ret = i40e_vsi_alloc_arrays(vsi);
+ if (ret)
+ goto err_netdev;
+
+ /* Rebuild q_vectors during VSI reinit because the effective channel
+ * count may change num_q_vectors. Keep vector topology aligned with the
+ * queue configuration after ethtool's .set_channels() callback.
+ */
+ ret = i40e_vsi_setup_vectors(vsi);
if (ret)
goto err_netdev;
@@ -14284,7 +14298,7 @@ static struct i40e_vsi *i40e_vsi_reinit_setup(struct i40e_vsi *vsi)
dev_info(&pf->pdev->dev,
"failed to get tracking for %d queues for VSI %d err %d\n",
alloc_queue_pairs, vsi->seid, ret);
- goto err_netdev;
+ goto err_rings;
}
vsi->base_queue = ret;
--
2.43.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* RE: [Intel-wired-lan] [PATCH v4 net 1/3] i40e: unregister netdev before clearing VSI on reinit failure
2026-06-25 15:14 ` [PATCH v4 net 1/3] i40e: unregister netdev before clearing VSI on reinit failure Maciej Fijalkowski
@ 2026-06-26 6:45 ` Loktionov, Aleksandr
2026-06-30 11:11 ` Maciej Fijalkowski
1 sibling, 0 replies; 7+ messages in thread
From: Loktionov, Aleksandr @ 2026-06-26 6:45 UTC (permalink / raw)
To: Fijalkowski, Maciej, intel-wired-lan@lists.osuosl.org
Cc: netdev@vger.kernel.org, Karlsson, Magnus, kuba@kernel.org,
pabeni@redhat.com, horms@kernel.org, Kitszel, Przemyslaw,
Keller, Jacob E, Fijalkowski, Maciej
> -----Original Message-----
> From: Intel-wired-lan <intel-wired-lan-bounces@osuosl.org> On Behalf
> Of Maciej Fijalkowski
> Sent: Thursday, June 25, 2026 5:14 PM
> To: intel-wired-lan@lists.osuosl.org
> Cc: netdev@vger.kernel.org; Karlsson, Magnus
> <magnus.karlsson@intel.com>; kuba@kernel.org; pabeni@redhat.com;
> horms@kernel.org; Kitszel, Przemyslaw <przemyslaw.kitszel@intel.com>;
> Keller, Jacob E <jacob.e.keller@intel.com>; Fijalkowski, Maciej
> <maciej.fijalkowski@intel.com>
> Subject: [Intel-wired-lan] [PATCH v4 net 1/3] i40e: unregister netdev
> before clearing VSI on reinit failure
>
> i40e_vsi_reinit_setup() tears down the existing VSI queue/ring backing
> state before allocating replacement arrays and queue tracking. If one
> of these early allocations fails, the function jumps directly to
> err_vsi and calls i40e_vsi_clear().
>
> For a registered netdev, this frees the VSI while netdev_priv(netdev)-
> >vsi can still point at it, leaving the registered netdev with
> dangling private driver state.
>
> Split the error path so failures after destructive reinit teardown
> first unregister and free the netdev before clearing the VSI.
>
> Fixes: d2a69fefd756 ("i40e: Fix changing previously set
> num_queue_pairs for PFs")
> Signed-off-by: Maciej Fijalkowski <maciej.fijalkowski@intel.com>
> ---
> drivers/net/ethernet/intel/i40e/i40e_main.c | 6 +++---
> 1 file changed, 3 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c
> b/drivers/net/ethernet/intel/i40e/i40e_main.c
> index a04683004a56..471fa7f7b643 100644
> --- a/drivers/net/ethernet/intel/i40e/i40e_main.c
> +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
> @@ -14274,7 +14274,7 @@ static struct i40e_vsi
> *i40e_vsi_reinit_setup(struct i40e_vsi *vsi)
> i40e_set_num_rings_in_vsi(vsi);
> ret = i40e_vsi_alloc_arrays(vsi, false);
> if (ret)
> - goto err_vsi;
> + goto err_netdev;
>
> alloc_queue_pairs = vsi->alloc_queue_pairs *
> (i40e_enabled_xdp_vsi(vsi) ? 2 : 1); @@ -
> 14284,7 +14284,7 @@ static struct i40e_vsi
> *i40e_vsi_reinit_setup(struct i40e_vsi *vsi)
> dev_info(&pf->pdev->dev,
> "failed to get tracking for %d queues for VSI %d
> err %d\n",
> alloc_queue_pairs, vsi->seid, ret);
> - goto err_vsi;
> + goto err_netdev;
> }
> vsi->base_queue = ret;
>
> @@ -14309,6 +14309,7 @@ static struct i40e_vsi
> *i40e_vsi_reinit_setup(struct i40e_vsi *vsi)
>
> err_rings:
> i40e_vsi_free_q_vectors(vsi);
> +err_netdev:
> if (vsi->netdev_registered) {
> vsi->netdev_registered = false;
> unregister_netdev(vsi->netdev);
> @@ -14318,7 +14319,6 @@ static struct i40e_vsi
> *i40e_vsi_reinit_setup(struct i40e_vsi *vsi)
> if (vsi->type == I40E_VSI_MAIN)
> i40e_devlink_destroy_port(pf);
> i40e_aq_delete_element(&pf->hw, vsi->seid, NULL);
> -err_vsi:
> i40e_vsi_clear(vsi);
> return NULL;
> }
> --
> 2.43.0
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
^ permalink raw reply [flat|nested] 7+ messages in thread
* RE: [Intel-wired-lan] [PATCH v4 net 2/3] i40e: fix potential UAF in i40e_vsi_setup()'s error path
2026-06-25 15:14 ` [PATCH v4 net 2/3] i40e: fix potential UAF in i40e_vsi_setup()'s error path Maciej Fijalkowski
@ 2026-06-26 6:45 ` Loktionov, Aleksandr
0 siblings, 0 replies; 7+ messages in thread
From: Loktionov, Aleksandr @ 2026-06-26 6:45 UTC (permalink / raw)
To: Fijalkowski, Maciej, intel-wired-lan@lists.osuosl.org
Cc: netdev@vger.kernel.org, Karlsson, Magnus, kuba@kernel.org,
pabeni@redhat.com, horms@kernel.org, Kitszel, Przemyslaw,
Keller, Jacob E, Fijalkowski, Maciej
> -----Original Message-----
> From: Intel-wired-lan <intel-wired-lan-bounces@osuosl.org> On Behalf
> Of Maciej Fijalkowski
> Sent: Thursday, June 25, 2026 5:15 PM
> To: intel-wired-lan@lists.osuosl.org
> Cc: netdev@vger.kernel.org; Karlsson, Magnus
> <magnus.karlsson@intel.com>; kuba@kernel.org; pabeni@redhat.com;
> horms@kernel.org; Kitszel, Przemyslaw <przemyslaw.kitszel@intel.com>;
> Keller, Jacob E <jacob.e.keller@intel.com>; Fijalkowski, Maciej
> <maciej.fijalkowski@intel.com>
> Subject: [Intel-wired-lan] [PATCH v4 net 2/3] i40e: fix potential UAF
> in i40e_vsi_setup()'s error path
>
> Sashiko pointed out an issue where error path in
> i40e_vsi_reinit_setup() released ring memory but then when freeing
> q_vectors, the rings mapped to q_vectors where touched which implies a
> regular use-after-free bug.
>
> Apparently i40e_vsi_setup() has the same problem, so swap the
> allocation and freeing order and fix the 13 year old bug.
>
> Fixes: 41c445ff0f48 ("i40e: main driver core")
> Signed-off-by: Maciej Fijalkowski <maciej.fijalkowski@intel.com>
> ---
> drivers/net/ethernet/intel/i40e/i40e_main.c | 12 ++++++------
> 1 file changed, 6 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c
> b/drivers/net/ethernet/intel/i40e/i40e_main.c
> index 471fa7f7b643..4adc7b0fb2f4 100644
> --- a/drivers/net/ethernet/intel/i40e/i40e_main.c
> +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
> @@ -14460,14 +14460,14 @@ struct i40e_vsi *i40e_vsi_setup(struct
> i40e_pf *pf, u8 type,
> fallthrough;
> case I40E_VSI_FDIR:
> /* set up vectors and rings if needed */
> - ret = i40e_vsi_setup_vectors(vsi);
> - if (ret)
> - goto err_msix;
> -
> ret = i40e_alloc_rings(vsi);
> if (ret)
> goto err_rings;
>
> + ret = i40e_vsi_setup_vectors(vsi);
> + if (ret)
> + goto err_qvec;
> +
> /* map all of the rings to the q_vectors */
> i40e_vsi_map_rings_to_vectors(vsi);
>
> @@ -14487,10 +14487,10 @@ struct i40e_vsi *i40e_vsi_setup(struct
> i40e_pf *pf, u8 type,
> return vsi;
>
> err_config:
> + i40e_vsi_free_q_vectors(vsi);
> +err_qvec:
> i40e_vsi_clear_rings(vsi);
> err_rings:
> - i40e_vsi_free_q_vectors(vsi);
> -err_msix:
> if (vsi->netdev_registered) {
> vsi->netdev_registered = false;
> unregister_netdev(vsi->netdev);
> --
> 2.43.0
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v4 net 1/3] i40e: unregister netdev before clearing VSI on reinit failure
2026-06-25 15:14 ` [PATCH v4 net 1/3] i40e: unregister netdev before clearing VSI on reinit failure Maciej Fijalkowski
2026-06-26 6:45 ` [Intel-wired-lan] " Loktionov, Aleksandr
@ 2026-06-30 11:11 ` Maciej Fijalkowski
1 sibling, 0 replies; 7+ messages in thread
From: Maciej Fijalkowski @ 2026-06-30 11:11 UTC (permalink / raw)
To: intel-wired-lan
Cc: netdev, magnus.karlsson, kuba, pabeni, horms, przemyslaw.kitszel,
jacob.e.keller
On Thu, Jun 25, 2026 at 05:14:29PM +0200, Maciej Fijalkowski wrote:
> i40e_vsi_reinit_setup() tears down the existing VSI queue/ring backing
> state before allocating replacement arrays and queue tracking. If one of
> these early allocations fails, the function jumps directly to err_vsi
> and calls i40e_vsi_clear().
>
> For a registered netdev, this frees the VSI while
> netdev_priv(netdev)->vsi can still point at it, leaving the registered
> netdev with dangling private driver state.
>
> Split the error path so failures after destructive reinit teardown first
> unregister and free the netdev before clearing the VSI.
>
> Fixes: d2a69fefd756 ("i40e: Fix changing previously set num_queue_pairs for PFs")
> Signed-off-by: Maciej Fijalkowski <maciej.fijalkowski@intel.com>
> ---
> drivers/net/ethernet/intel/i40e/i40e_main.c | 6 +++---
> 1 file changed, 3 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
> index a04683004a56..471fa7f7b643 100644
> --- a/drivers/net/ethernet/intel/i40e/i40e_main.c
> +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
> @@ -14274,7 +14274,7 @@ static struct i40e_vsi *i40e_vsi_reinit_setup(struct i40e_vsi *vsi)
> i40e_set_num_rings_in_vsi(vsi);
> ret = i40e_vsi_alloc_arrays(vsi, false);
> if (ret)
> - goto err_vsi;
> + goto err_netdev;
>
> alloc_queue_pairs = vsi->alloc_queue_pairs *
> (i40e_enabled_xdp_vsi(vsi) ? 2 : 1);
> @@ -14284,7 +14284,7 @@ static struct i40e_vsi *i40e_vsi_reinit_setup(struct i40e_vsi *vsi)
> dev_info(&pf->pdev->dev,
> "failed to get tracking for %d queues for VSI %d err %d\n",
> alloc_queue_pairs, vsi->seid, ret);
> - goto err_vsi;
> + goto err_netdev;
> }
> vsi->base_queue = ret;
>
> @@ -14309,6 +14309,7 @@ static struct i40e_vsi *i40e_vsi_reinit_setup(struct i40e_vsi *vsi)
>
> err_rings:
> i40e_vsi_free_q_vectors(vsi);
> +err_netdev:
> if (vsi->netdev_registered) {
> vsi->netdev_registered = false;
> unregister_netdev(vsi->netdev);
Sashiko says:
---
Could this result in a deadlock when called during a device rebuild?
Looking at i40e_rebuild(), it explicitly acquires the RTNL lock before
proceeding:
drivers/net/ethernet/intel/i40e/i40e_main.c:i40e_rebuild() {
...
if (!lock_acquired)
rtnl_lock();
ret = i40e_setup_pf_switch(pf, reinit, true);
...
}
If i40e_setup_pf_switch() calls i40e_vsi_reinit_setup() and takes this new
err_netdev path, unregister_netdev() will unconditionally attempt to acquire
rtnl_lock(), leading to a deadlock on the non-recursive mutex.
---
which is another valid concern. I'll take a stab at addressing this, but
looking at a bigger picture, we don't propagate errors from rebuild path,
so I wouldn't be surprised that in the next iteration Sashiko would point
it out. I'd say that would be a too big refactor for this series.
> @@ -14318,7 +14319,6 @@ static struct i40e_vsi *i40e_vsi_reinit_setup(struct i40e_vsi *vsi)
> if (vsi->type == I40E_VSI_MAIN)
> i40e_devlink_destroy_port(pf);
> i40e_aq_delete_element(&pf->hw, vsi->seid, NULL);
> -err_vsi:
> i40e_vsi_clear(vsi);
> return NULL;
> }
> --
> 2.43.0
>
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-06-30 11:11 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-25 15:14 [PATCH v4 net 0/3] i40e: re-init and UAF fixes Maciej Fijalkowski
2026-06-25 15:14 ` [PATCH v4 net 1/3] i40e: unregister netdev before clearing VSI on reinit failure Maciej Fijalkowski
2026-06-26 6:45 ` [Intel-wired-lan] " Loktionov, Aleksandr
2026-06-30 11:11 ` Maciej Fijalkowski
2026-06-25 15:14 ` [PATCH v4 net 2/3] i40e: fix potential UAF in i40e_vsi_setup()'s error path Maciej Fijalkowski
2026-06-26 6:45 ` [Intel-wired-lan] " Loktionov, Aleksandr
2026-06-25 15:14 ` [PATCH v4 net 3/3] i40e: keep q_vectors array in sync with channel count changes Maciej Fijalkowski
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox