From: Dave Ertman <[email protected]>

With a chatty lldpad, DCB configuration requests can arrive through
the DCBNL API while the driver is tearing down PF resources, leading
to use-after-free and NULL dereference crashes.

Set ICE_SHUTTING_DOWN in pf->state at the start of ice_remove() and
check this bit at the beginning of every DCBNL callback that accesses
resources freed during the remove path.

Fixes: b94b013eb626 ("ice: Implement DCBNL support")
Cc: [email protected]
Signed-off-by: Dave Ertman <[email protected]>
Signed-off-by: Aleksandr Loktionov <[email protected]>
---

 drivers/net/ethernet/intel/ice/ice.h        |  1 +
 drivers/net/ethernet/intel/ice/ice_dcb_nl.c | 46 +++++++++++++++++++++
 drivers/net/ethernet/intel/ice/ice_main.c   |  1 +
 3 files changed, 48 insertions(+)

diff --git a/drivers/net/ethernet/intel/ice/ice.h 
b/drivers/net/ethernet/intel/ice/ice.h
index 2b2b22a..052c310 100644
--- a/drivers/net/ethernet/intel/ice/ice.h
+++ b/drivers/net/ethernet/intel/ice/ice.h
@@ -283,6 +283,7 @@ enum ice_pf_state {
        ICE_EMPR_RECV,          /* set by OICR handler */
        ICE_SUSPENDED,          /* set on module remove path */
        ICE_RESET_FAILED,               /* set by reset/rebuild */
+       ICE_SHUTTING_DOWN,              /* set on module remove path, before 
releasing resources */
        /* When checking for the PF to be in a nominal operating state, the
         * bits that are grouped at the beginning of the list need to be
         * checked. Bits occurring before ICE_STATE_NOMINAL_CHECK_BITS will
diff --git a/drivers/net/ethernet/intel/ice/ice_dcb_nl.c 
b/drivers/net/ethernet/intel/ice/ice_dcb_nl.c
index a10c1c8d..4a30129 100644
--- a/drivers/net/ethernet/intel/ice/ice_dcb_nl.c
+++ b/drivers/net/ethernet/intel/ice/ice_dcb_nl.c
@@ -15,6 +15,8 @@ static void ice_dcbnl_devreset(struct net_device *netdev)
 {
        struct ice_pf *pf = ice_netdev_to_pf(netdev);
 
+       if (test_bit(ICE_SHUTTING_DOWN, pf->state))
+               return;
        while (ice_is_reset_in_progress(pf->state))
                usleep_range(1000, 2000);
 
@@ -35,6 +37,8 @@ static int ice_dcbnl_getets(struct net_device *netdev, struct 
ieee_ets *ets)
        struct ice_pf *pf;
 
        pf = ice_netdev_to_pf(netdev);
+       if (test_bit(ICE_SHUTTING_DOWN, pf->state))
+               return -EBUSY;
        dcbxcfg = &pf->hw.port_info->qos_cfg.local_dcbx_cfg;
 
        ets->willing = dcbxcfg->etscfg.willing;
@@ -66,6 +70,8 @@ static int ice_dcbnl_setets(struct net_device *netdev, struct 
ieee_ets *ets)
        int bwcfg = 0, bwrec = 0;
        int err, i;
 
+       if (test_bit(ICE_SHUTTING_DOWN, pf->state))
+               return -EBUSY;
        if ((pf->dcbx_cap & DCB_CAP_DCBX_LLD_MANAGED) ||
            !(pf->dcbx_cap & DCB_CAP_DCBX_VER_IEEE))
                return -EINVAL;
@@ -135,6 +141,8 @@ ice_dcbnl_getnumtcs(struct net_device *dev, int 
__always_unused tcid, u8 *num)
 
        if (!test_bit(ICE_FLAG_DCB_CAPABLE, pf->flags))
                return -EINVAL;
+       if (test_bit(ICE_SHUTTING_DOWN, pf->state))
+               return -EBUSY;
 
        *num = pf->hw.func_caps.common_cap.maxtc;
        return 0;
@@ -148,6 +156,8 @@ static u8 ice_dcbnl_getdcbx(struct net_device *netdev)
 {
        struct ice_pf *pf = ice_netdev_to_pf(netdev);
 
+       if (test_bit(ICE_SHUTTING_DOWN, pf->state))
+               return 0;
        return pf->dcbx_cap;
 }
 
@@ -161,6 +171,8 @@ static u8 ice_dcbnl_setdcbx(struct net_device *netdev, u8 
mode)
        struct ice_pf *pf = ice_netdev_to_pf(netdev);
        struct ice_qos_cfg *qos_cfg;
 
+       if (test_bit(ICE_SHUTTING_DOWN, pf->state))
+               return 0;
        /* if FW LLDP agent is running, DCBNL not allowed to change mode */
        if (test_bit(ICE_FLAG_FW_LLDP_AGENT, pf->flags))
                return ICE_DCB_NO_HW_CHG;
@@ -208,6 +220,8 @@ static void ice_dcbnl_get_perm_hw_addr(struct net_device 
*netdev, u8 *perm_addr)
        struct ice_port_info *pi = pf->hw.port_info;
        int i, j;
 
+       if (test_bit(ICE_SHUTTING_DOWN, pf->state))
+               return;
        memset(perm_addr, 0xff, MAX_ADDR_LEN);
 
        for (i = 0; i < netdev->addr_len; i++)
@@ -242,6 +256,8 @@ static int ice_dcbnl_getpfc(struct net_device *netdev, 
struct ieee_pfc *pfc)
        struct ice_dcbx_cfg *dcbxcfg;
        int i;
 
+       if (test_bit(ICE_SHUTTING_DOWN, pf->state))
+               return -EBUSY;
        dcbxcfg = &pi->qos_cfg.local_dcbx_cfg;
        pfc->pfc_cap = dcbxcfg->pfc.pfccap;
        pfc->pfc_en = dcbxcfg->pfc.pfcena;
@@ -267,6 +283,8 @@ static int ice_dcbnl_setpfc(struct net_device *netdev, 
struct ieee_pfc *pfc)
        struct ice_dcbx_cfg *new_cfg;
        int err;
 
+       if (test_bit(ICE_SHUTTING_DOWN, pf->state))
+               return -EBUSY;
        if ((pf->dcbx_cap & DCB_CAP_DCBX_LLD_MANAGED) ||
            !(pf->dcbx_cap & DCB_CAP_DCBX_VER_IEEE))
                return -EINVAL;
@@ -308,6 +326,8 @@ ice_dcbnl_get_pfc_cfg(struct net_device *netdev, int prio, 
u8 *setting)
        struct ice_pf *pf = ice_netdev_to_pf(netdev);
        struct ice_port_info *pi = pf->hw.port_info;
 
+       if (test_bit(ICE_SHUTTING_DOWN, pf->state))
+               return;
        if ((pf->dcbx_cap & DCB_CAP_DCBX_LLD_MANAGED) ||
            !(pf->dcbx_cap & DCB_CAP_DCBX_VER_CEE))
                return;
@@ -331,6 +351,8 @@ static void ice_dcbnl_set_pfc_cfg(struct net_device 
*netdev, int prio, u8 set)
        struct ice_pf *pf = ice_netdev_to_pf(netdev);
        struct ice_dcbx_cfg *new_cfg;
 
+       if (test_bit(ICE_SHUTTING_DOWN, pf->state))
+               return;
        if ((pf->dcbx_cap & DCB_CAP_DCBX_LLD_MANAGED) ||
            !(pf->dcbx_cap & DCB_CAP_DCBX_VER_CEE))
                return;
@@ -364,6 +386,8 @@ static u8 ice_dcbnl_getpfcstate(struct net_device *netdev)
        struct ice_pf *pf = ice_netdev_to_pf(netdev);
        struct ice_port_info *pi = pf->hw.port_info;
 
+       if (test_bit(ICE_SHUTTING_DOWN, pf->state))
+               return 0;
        /* Return enabled if any UP enabled for PFC */
        if (pi->qos_cfg.local_dcbx_cfg.pfc.pfcena)
                return 1;
@@ -395,6 +419,8 @@ static u8 ice_dcbnl_setstate(struct net_device *netdev, u8 
state)
 {
        struct ice_pf *pf = ice_netdev_to_pf(netdev);
 
+       if (test_bit(ICE_SHUTTING_DOWN, pf->state))
+               return ICE_DCB_NO_HW_CHG;
        if ((pf->dcbx_cap & DCB_CAP_DCBX_LLD_MANAGED) ||
            !(pf->dcbx_cap & DCB_CAP_DCBX_VER_CEE))
                return ICE_DCB_NO_HW_CHG;
@@ -469,6 +495,8 @@ ice_dcbnl_set_pg_tc_cfg_tx(struct net_device *netdev, int 
tc,
        struct ice_dcbx_cfg *new_cfg;
        int i;
 
+       if (test_bit(ICE_SHUTTING_DOWN, pf->state))
+               return;
        if ((pf->dcbx_cap & DCB_CAP_DCBX_LLD_MANAGED) ||
            !(pf->dcbx_cap & DCB_CAP_DCBX_VER_CEE))
                return;
@@ -504,6 +532,8 @@ ice_dcbnl_get_pg_bwg_cfg_tx(struct net_device *netdev, int 
pgid, u8 *bw_pct)
        struct ice_pf *pf = ice_netdev_to_pf(netdev);
        struct ice_port_info *pi = pf->hw.port_info;
 
+       if (test_bit(ICE_SHUTTING_DOWN, pf->state))
+               return;
        if ((pf->dcbx_cap & DCB_CAP_DCBX_LLD_MANAGED) ||
            !(pf->dcbx_cap & DCB_CAP_DCBX_VER_CEE))
                return;
@@ -528,6 +558,8 @@ ice_dcbnl_set_pg_bwg_cfg_tx(struct net_device *netdev, int 
pgid, u8 bw_pct)
        struct ice_pf *pf = ice_netdev_to_pf(netdev);
        struct ice_dcbx_cfg *new_cfg;
 
+       if (test_bit(ICE_SHUTTING_DOWN, pf->state))
+               return;
        if ((pf->dcbx_cap & DCB_CAP_DCBX_LLD_MANAGED) ||
            !(pf->dcbx_cap & DCB_CAP_DCBX_VER_CEE))
                return;
@@ -609,6 +641,8 @@ ice_dcbnl_get_pg_bwg_cfg_rx(struct net_device *netdev, int 
__always_unused pgid,
 {
        struct ice_pf *pf = ice_netdev_to_pf(netdev);
 
+       if (test_bit(ICE_SHUTTING_DOWN, pf->state))
+               return;
        if ((pf->dcbx_cap & DCB_CAP_DCBX_LLD_MANAGED) ||
            !(pf->dcbx_cap & DCB_CAP_DCBX_VER_CEE))
                return;
@@ -643,6 +677,8 @@ static u8 ice_dcbnl_get_cap(struct net_device *netdev, int 
capid, u8 *cap)
 {
        struct ice_pf *pf = ice_netdev_to_pf(netdev);
 
+       if (test_bit(ICE_SHUTTING_DOWN, pf->state))
+               return ICE_DCB_NO_HW_CHG;
        if (!(test_bit(ICE_FLAG_DCB_CAPABLE, pf->flags)))
                return ICE_DCB_NO_HW_CHG;
 
@@ -695,6 +731,8 @@ static int ice_dcbnl_getapp(struct net_device *netdev, u8 
idtype, u16 id)
                                .protocol = id,
                             };
 
+       if (test_bit(ICE_SHUTTING_DOWN, pf->state))
+               return -EBUSY;
        if ((pf->dcbx_cap & DCB_CAP_DCBX_LLD_MANAGED) ||
            !(pf->dcbx_cap & DCB_CAP_DCBX_VER_CEE))
                return -EINVAL;
@@ -738,6 +776,8 @@ static int ice_dcbnl_setapp(struct net_device *netdev, 
struct dcb_app *app)
        u8 max_tc;
        int ret;
 
+       if (test_bit(ICE_SHUTTING_DOWN, pf->state))
+               return -EBUSY;
        /* ONLY DSCP APP TLVs have operational significance */
        if (app->selector != IEEE_8021QAZ_APP_SEL_DSCP)
                return -EINVAL;
@@ -871,6 +911,8 @@ static int ice_dcbnl_delapp(struct net_device *netdev, 
struct dcb_app *app)
        unsigned int i, j;
        int ret = 0;
 
+       if (test_bit(ICE_SHUTTING_DOWN, pf->state))
+               return -EBUSY;
        if (pf->dcbx_cap & DCB_CAP_DCBX_LLD_MANAGED) {
                netdev_err(netdev, "can't delete DSCP netlink app when FW DCB 
agent is active\n");
                return -EINVAL;
@@ -978,6 +1020,8 @@ static u8 ice_dcbnl_cee_set_all(struct net_device *netdev)
        struct ice_dcbx_cfg *new_cfg;
        int err;
 
+       if (test_bit(ICE_SHUTTING_DOWN, pf->state))
+               return ICE_DCB_NO_HW_CHG;
        if ((pf->dcbx_cap & DCB_CAP_DCBX_LLD_MANAGED) ||
            !(pf->dcbx_cap & DCB_CAP_DCBX_VER_CEE))
                return ICE_DCB_NO_HW_CHG;
@@ -1048,6 +1092,8 @@ void ice_dcbnl_set_all(struct ice_vsi *vsi)
                return;
 
        pf = ice_netdev_to_pf(netdev);
+       if (test_bit(ICE_SHUTTING_DOWN, pf->state))
+               return;
        pi = pf->hw.port_info;
 
        /* SW DCB taken care of by SW Default Config */
diff --git a/drivers/net/ethernet/intel/ice/ice_main.c 
b/drivers/net/ethernet/intel/ice/ice_main.c
index 685c9618..eff48bd 100644
--- a/drivers/net/ethernet/intel/ice/ice_main.c
+++ b/drivers/net/ethernet/intel/ice/ice_main.c
@@ -5424,6 +5424,7 @@ static void ice_remove(struct pci_dev *pdev)
        struct ice_pf *pf = pci_get_drvdata(pdev);
        int i;
 
+       set_bit(ICE_SHUTTING_DOWN, pf->state);
        for (i = 0; i < ICE_MAX_RESET_WAIT; i++) {
                if (!ice_is_reset_in_progress(pf->state))
                        break;
-- 
2.52.0

Reply via email to