pnet_find_base_ndev() resolves the base net_device for stacked devices under RTNL, then drops the lock and returns the raw pointer without taking a reference. All three callers dereference the pointer after RTNL has been released, creating a use-after-free window if the device is concurrently unregistered. Take dev_hold() before dropping RTNL and add the matching dev_put() in every return path of the three callers: - smc_pnet_add_eth() - smc_pnet_find_roce_by_pnetid() - smc_pnet_find_ism_by_pnetid() Fixes: 0afff91c6f5e ("net/smc: add pnetid support") Fixes: 1619f770589a ("net/smc: add pnetid support for SMC-D and ISM") Cc: "D. Wythe" Cc: Dust Li Cc: Sidraya Jayagond Cc: Mahanta Jambigi Cc: Tony Lu Cc: Wen Gu Cc: Willy Tarreau Cc: linux-rdma@vger.kernel.org Cc: linux-s390@vger.kernel.org Cc: netdev@vger.kernel.org Signed-off-by: Atharva Vartak --- Apologies for not Cc'ing the maintainers on the initial report, will follow the process properly going forward. Based on v7.3-rc5. Passes checkpatch and compiles cleanly. net/smc/smc_pnet.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/net/smc/smc_pnet.c b/net/smc/smc_pnet.c index ff9c9c35c..d6e2f4454 100644 --- a/net/smc/smc_pnet.c +++ b/net/smc/smc_pnet.c @@ -367,8 +367,11 @@ static int smc_pnet_add_eth(struct smc_pnettable *pnettable, struct net *net, if (ndev) { base_ndev = pnet_find_base_ndev(ndev); if (!smc_pnetid_by_dev_port(base_ndev->dev.parent, - base_ndev->dev_port, ndev_pnetid)) + base_ndev->dev_port, ndev_pnetid)) { + dev_put(base_ndev); goto out_put; + } + dev_put(base_ndev); } /* add a new netdev entry to the pnet table if there isn't one */ @@ -949,6 +952,8 @@ static struct net_device *pnet_find_base_ndev(struct net_device *ndev) { rtnl_lock(); ndev = __pnet_find_base_ndev(ndev); + /* keep ndev alive after dropping RTNL, callers must dev_put() */ + dev_hold(ndev); rtnl_unlock(); return ndev; } @@ -1094,8 +1099,10 @@ static void smc_pnet_find_roce_by_pnetid(struct net_device *ndev, smc_pnet_find_ndev_pnetid_by_table(base_ndev, ndev_pnetid) && smc_pnet_find_ndev_pnetid_by_table(ndev, ndev_pnetid)) { smc_pnet_find_rdma_dev(base_ndev, ini); + dev_put(base_ndev); return; /* pnetid could not be determined */ } + dev_put(base_ndev); _smc_pnet_find_roce_by_pnetid(ndev_pnetid, ini, NULL, net); } @@ -1108,8 +1115,10 @@ static void smc_pnet_find_ism_by_pnetid(struct net_device *ndev, ndev = pnet_find_base_ndev(ndev); if (smc_pnetid_by_dev_port(ndev->dev.parent, ndev->dev_port, ndev_pnetid) && - smc_pnet_find_ndev_pnetid_by_table(ndev, ndev_pnetid)) + smc_pnet_find_ndev_pnetid_by_table(ndev, ndev_pnetid)) { + dev_put(ndev); return; /* pnetid could not be determined */ + } mutex_lock(&smcd_dev_list.mutex); list_for_each_entry(ismdev, &smcd_dev_list.list, list) { @@ -1123,6 +1132,7 @@ static void smc_pnet_find_ism_by_pnetid(struct net_device *ndev, } } mutex_unlock(&smcd_dev_list.mutex); + dev_put(ndev); } /* PNET table analysis for a given sock: -- 2.54.0