ice_tear_down_devlink_rate_tree() frees the devlink rate nodes with
devl_rate_nodes_destroy(), but leaves ice_sched_node::rate_node pointing at
them. The pointers are only reset when the last representor goes away, in
ice_eswitch_detach().

If DCB is turned on and off again while representors exist, the next
ice_devlink_rate_init_tx_topology() (e.g. on adding an SF port) sees the
stale pointers as already exported nodes and passes one of them as the
parent to devl_rate_leaf_create(), which writes to freed memory.

Clear the pointers before destroying the nodes, as ice_eswitch_detach() does.

dcbnl ops can call ice_tear_down_devlink_rate_tree() while a reset is in
flight, or after a failed one, when the Tx scheduler tree is gone. Make
ice_devlink_rate_clear_tx_topology() skip the clearing then, as other Tx
scheduler helpers do when the port is not ready.

Reproducer:
  PF=eth3
  BDF=$(basename $(readlink /sys/class/net/$PF/device))
  ethtool --set-priv-flags $PF fw-lldp-agent off
  devlink dev eswitch set pci/$BDF mode switchdev
  echo 1 > /sys/class/net/$PF/device/sriov_numvfs
  dcb ets set dev $PF tc-tsa all:ets tc-bw all:0 0:50 1:50 prio-tc all:0 4:1
  dcb ets set dev $PF tc-tsa all:ets tc-bw all:0 0:100 prio-tc all:0
  devlink port add pci/$BDF flavour pcisf pfnum 0 sfnum 1

  BUG: KASAN: slab-use-after-free in devl_rate_leaf_create+0x109/0x390

Fixes: 86197ad5800b ("ice: set Tx topology every time new repr is added")
Signed-off-by: Przemek Kitszel <[email protected]>
---
 drivers/net/ethernet/intel/ice/devlink/devlink.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/drivers/net/ethernet/intel/ice/devlink/devlink.c 
b/drivers/net/ethernet/intel/ice/devlink/devlink.c
index 8c2b63eef82b..044aa62be473 100644
--- a/drivers/net/ethernet/intel/ice/devlink/devlink.c
+++ b/drivers/net/ethernet/intel/ice/devlink/devlink.c
@@ -705,6 +705,7 @@ void ice_tear_down_devlink_rate_tree(struct ice_pf *pf)
        }
        mutex_unlock(&pf->vfs.table_lock);
 
+       ice_devlink_rate_clear_tx_topology(ice_get_main_vsi(pf));
        devl_rate_nodes_destroy(devlink);
        devl_unlock(devlink);
 }
@@ -842,6 +843,10 @@ void ice_devlink_rate_clear_tx_topology(struct ice_vsi 
*vsi)
 {
        struct ice_port_info *pi = vsi->port_info;
 
+       /* the Tx scheduler tree is gone during a reset */
+       if (pi->port_state != ICE_SCHED_PORT_STATE_READY)
+               return;
+
        mutex_lock(&pi->sched_lock);
        ice_clear_rate_nodes(pi->root->children[0]);
        mutex_unlock(&pi->sched_lock);
-- 
2.54.0

Reply via email to