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