Thank you for your contribution! Sashiko AI review found 1 potential issue(s) 
to consider:

Pre-existing issues:
- [High] Use-After-Free of CQ in EQ event processing due to lack of 
synchronization during CQ destruction
--

commit 3d0d6fcf405f01d902d109c4441be576c316986f
Author: Long Li <[email protected]>
net: mana: release EQs left idle by a channel-count reduction

The shared EQ pool only grows, so it sits at the high-water mark of every
channel count the port has ever used. This commit releases the EQs above the
live queue count once a retiring set has been torn down. It also ensures the
dentry is properly recorded for debugfs.

> diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c 
> b/drivers/net/ethernet/microsoft/mana/mana_en.c
> index 69e34fab8fe8e..2295769da0306 100644
> --- a/drivers/net/ethernet/microsoft/mana/mana_en.c
> +++ b/drivers/net/ethernet/microsoft/mana/mana_en.c
[ ... ]
> @@ -4745,12 +4785,19 @@ void mana_free_qset(struct mana_port_context 
> *scratch, struct mana_qset *qset)
>  
>       memset(qset, 0, sizeof(*qset));
>  
> +     /* This set is gone, so any EQ above the live queue count is now
> +      * unreferenced. Release those vectors instead of holding them at the
> +      * high-water mark. Safe here and only here: the retiring set's CQs
> +      * have just been destroyed.
> +      */

[Severity: High]
This is a pre-existing issue, but since this teardown path is being modified,
can the destruction of the retiring set's CQs race with the EQ interrupt
handler?

An admin reconfiguring the network interface (e.g., changing MTU, ring sizes,
or channels) will tear down the old queue set. The hardware queues are fenced,
generating a final CQE in the CQ.

If the EQ interrupt fires, CPU A could process the EQE in
mana_gd_process_eqe(). It reads the CQ pointer but might be delayed (e.g., by
an NMI) before invoking the callback:

drivers/net/ethernet/microsoft/mana/gdma_main.c:mana_gd_process_eqe() {
    ...
    cq = gc->cq_table[cq_id];
    if (WARN_ON_ONCE(!cq || cq->type != GDMA_CQ || cq->id != cq_id))
        break;

    if (cq->cq.callback)
        cq->cq.callback(cq->cq.context, cq);
    ...
}

Concurrently, NAPI on CPU C could process the fence CQE and signal the
completion event. CPU B, waiting in mana_fence_rqs(), wakes up and tears down
the old CQ via mana_destroy_rxq() -> mana_gd_destroy_queue().

It clears gc->cq_table[cq_id] = NULL in mana_gd_destroy_cq() and frees the CQ
memory immediately in mana_gd_destroy_queue():

drivers/net/ethernet/microsoft/mana/gdma_main.c:mana_gd_destroy_queue() {
    ...
    mana_gd_destroy_cq(gc, queue);
    ...
    kfree(queue);
}

When CPU A resumes and executes cq->cq.callback(cq->cq.context, cq), this
dereferences the freed cq and mana_rxq structures to call napi_schedule(),
causing a UAF write to napi.state.

Is there any synchronization, such as RCU or IRQ synchronization, to prevent
this use-after-free during CQ destruction?

> +     mana_shrink_eqs(apc, apc->num_queues);
> +
>       /* Queues built through a scratch context carry no debugfs nodes,
>        * because both sets are alive during the swap and would collide on
>        * the same names. The retiring set's nodes are gone now, so the
>        * published queues can finally take those names.
>        */
> -     mana_qset_debugfs_publish(netdev_priv(scratch->ndev));
> +     mana_qset_debugfs_publish(apc);
>  }
>  
>  /* --- end of pre-allocate + swap reconfiguration path 
> ---------------------- */

-- 
Sashiko AI review ยท 
https://sashiko.dev/#/patchset/[email protected]?part=10

Reply via email to