On Thu, Nov 05, 2026 at 09:47:22AM +0000, Anurag Mandal wrote:
> ice_dcf_hierarchy_commit() keeps a copy of the bandwidth config
> of each VF, and of the TC nodes, in qos_bw_cfg[] to replay it
> after a VF reset.
> Each commit allocates new copies without freeing the old ones,
> and ice_dcf_uninit_hw() frees the array but not the copies, so
> they also leak on close and reset.
>
> Free the old copy before allocating a new one, and free all copies
> in ice_dcf_uninit_hw().
>
> Fixes: 931ee54072b1 ("net/ice: support QoS bandwidth config after VF reset in
> DCF")
> Fixes: 775a25a372f5 ("net/ice: clear QoS bandwidth on DCF close")
> Cc: [email protected]
>
> Signed-off-by: Anurag Mandal <[email protected]>
> ---
> drivers/net/intel/ice/ice_dcf.c | 6 ++++++
> drivers/net/intel/ice/ice_dcf_sched.c | 2 ++
> 2 files changed, 8 insertions(+)
>
> diff --git a/drivers/net/intel/ice/ice_dcf.c b/drivers/net/intel/ice/ice_dcf.c
> index 09fcf6454f..c2b557d0da 100644
> --- a/drivers/net/intel/ice/ice_dcf.c
> +++ b/drivers/net/intel/ice/ice_dcf.c
> @@ -884,6 +884,7 @@ ice_dcf_uninit_hw(struct rte_eth_dev *eth_dev, struct
> ice_dcf_hw *hw)
> {
> struct rte_pci_device *pci_dev = RTE_CLASS_TO_BUS_DEVICE(eth_dev,
> *pci_dev);
> struct rte_intr_handle *intr_handle = pci_dev->intr_handle;
> + uint16_t i;
>
> if (hw->vf_res->vf_cap_flags & VIRTCHNL_VF_OFFLOAD_QOS)
> if (hw->tm_conf.committed) {
> @@ -919,6 +920,11 @@ ice_dcf_uninit_hw(struct rte_eth_dev *eth_dev, struct
> ice_dcf_hw *hw)
> rte_free(hw->rss_key);
> hw->rss_key = NULL;
>
> + if (hw->qos_bw_cfg != NULL) {
> + for (i = 0; i < hw->num_vfs; i++)
> + rte_free(hw->qos_bw_cfg[i]);
> + }
> +
> rte_free(hw->qos_bw_cfg);
> hw->qos_bw_cfg = NULL;
>
> diff --git a/drivers/net/intel/ice/ice_dcf_sched.c
> b/drivers/net/intel/ice/ice_dcf_sched.c
> index de28019c5b..f8755c7dcb 100644
> --- a/drivers/net/intel/ice/ice_dcf_sched.c
> +++ b/drivers/net/intel/ice/ice_dcf_sched.c
> @@ -813,6 +813,7 @@ static int ice_dcf_hierarchy_commit(struct rte_eth_dev
> *dev,
> if (ret_val)
> goto fail_clear;
>
> + rte_free(hw->qos_bw_cfg[vf_id]);
> hw->qos_bw_cfg[vf_id] = rte_zmalloc("vf_bw_cfg", size, 0);
> if (!hw->qos_bw_cfg[vf_id]) {
> ret_val = ICE_ERR_NO_MEMORY;
> @@ -840,6 +841,7 @@ static int ice_dcf_hierarchy_commit(struct rte_eth_dev
> *dev,
> goto fail_clear;
>
> /* store TC node bw configuration */
> + rte_free(hw->qos_bw_cfg[ICE_DCF_VFID]);
> hw->qos_bw_cfg[ICE_DCF_VFID] = rte_zmalloc("tc_bw_cfg", size, 0);
> if (!hw->qos_bw_cfg[ICE_DCF_VFID]) {
> ret_val = ICE_ERR_NO_MEMORY;
> --
> 2.25.1
>
AI review flags this:
The new free can race with VF-reset replay and cause a use-after-free. The
reset worker calls ice_dcf_parent.c:152-153, which passes the cached buffer
to the virtchnl command (ice_dcf_sched.c:681-686); meanwhile the hierarchy
commit frees and replaces that buffer (ice_dcf_sched.c:816-823). The reset
worker’s vsi_update_lock does not protect the commit path. Serialize
replacement with replay, or copy the replay data while holding a shared
lock.