From b778e445689fc3830650412bc45a5a47901ea8a1 Mon Sep 17 00:00:00 2001 From: Daniel Stodden Date: Mon, 8 Apr 2024 14:49:44 +0800 Subject: [PATCH 1/2] PCI: switchtec: Fix stdev_release() crash after surprise hot remove mainline inclusion from mainline-v6.8-rc1 commit df25461119d987b8c81d232cfe4411e91dcabe66 category: bugfix bugzilla: https://gitee.com/src-openeuler/kernel/issues/I99JXH CVE: CVE-2023-52617 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit?id=df25461119d987b8c81d232cfe4411e91dcabe66 -------------------------------- A PCI device hot removal may occur while stdev->cdev is held open. The call to stdev_release() then happens during close or exit, at a point way past switchtec_pci_remove(). Otherwise the last ref would vanish with the trailing put_device(), just before return. At that later point in time, the devm cleanup has already removed the stdev->mmio_mrpc mapping. Also, the stdev->pdev reference was not a counted one. Therefore, in DMA mode, the iowrite32() in stdev_release() will cause a fatal page fault, and the subsequent dma_free_coherent(), if reached, would pass a stale &stdev->pdev->dev pointer. Fix by moving MRPC DMA shutdown into switchtec_pci_remove(), after stdev_kill(). Counting the stdev->pdev ref is now optional, but may prevent future accidents. Reproducible via the script at https://lore.kernel.org/r/20231113212150.96410-1-dns@arista.com Link: https://lore.kernel.org/r/20231122042316.91208-2-dns@arista.com Signed-off-by: Daniel Stodden Signed-off-by: Bjorn Helgaas Reviewed-by: Logan Gunthorpe Reviewed-by: Dmitry Safonov Signed-off-by: Xiongfeng Wang --- drivers/pci/switch/switchtec.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/drivers/pci/switch/switchtec.c b/drivers/pci/switch/switchtec.c index ba52459928f7..5cea3ad290c5 100644 --- a/drivers/pci/switch/switchtec.c +++ b/drivers/pci/switch/switchtec.c @@ -1251,13 +1251,6 @@ static void stdev_release(struct device *dev) { struct switchtec_dev *stdev = to_stdev(dev); - if (stdev->dma_mrpc) { - iowrite32(0, &stdev->mmio_mrpc->dma_en); - flush_wc_buf(stdev); - writeq(0, &stdev->mmio_mrpc->dma_addr); - dma_free_coherent(&stdev->pdev->dev, sizeof(*stdev->dma_mrpc), - stdev->dma_mrpc, stdev->dma_mrpc_dma_addr); - } kfree(stdev); } @@ -1301,7 +1294,7 @@ static struct switchtec_dev *stdev_create(struct pci_dev *pdev) return ERR_PTR(-ENOMEM); stdev->alive = true; - stdev->pdev = pdev; + stdev->pdev = pci_dev_get(pdev); INIT_LIST_HEAD(&stdev->mrpc_queue); mutex_init(&stdev->mrpc_mutex); stdev->mrpc_busy = 0; @@ -1335,6 +1328,7 @@ static struct switchtec_dev *stdev_create(struct pci_dev *pdev) return stdev; err_put: + pci_dev_put(stdev->pdev); put_device(&stdev->dev); return ERR_PTR(rc); } @@ -1587,6 +1581,18 @@ static int switchtec_init_pci(struct switchtec_dev *stdev, return 0; } +static void switchtec_exit_pci(struct switchtec_dev *stdev) +{ + if (stdev->dma_mrpc) { + iowrite32(0, &stdev->mmio_mrpc->dma_en); + flush_wc_buf(stdev); + writeq(0, &stdev->mmio_mrpc->dma_addr); + dma_free_coherent(&stdev->pdev->dev, sizeof(*stdev->dma_mrpc), + stdev->dma_mrpc, stdev->dma_mrpc_dma_addr); + stdev->dma_mrpc = NULL; + } +} + static int switchtec_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { @@ -1646,6 +1652,9 @@ static void switchtec_pci_remove(struct pci_dev *pdev) ida_simple_remove(&switchtec_minor_ida, MINOR(stdev->dev.devt)); dev_info(&stdev->dev, "unregistered.\n"); stdev_kill(stdev); + switchtec_exit_pci(stdev); + pci_dev_put(stdev->pdev); + stdev->pdev = NULL; put_device(&stdev->dev); } -- Gitee From 6bf196a086b9846438c17ee23d1d8ccf5eaf1df9 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Mon, 8 Apr 2024 14:49:45 +0800 Subject: [PATCH 2/2] PCI: switchtec: Fix an error handling path in switchtec_pci_probe() mainline inclusion from mainline-v6.8-rc1 commit dec529b0b0572b32f9eb91c882dd1f08ca657efb category: bugfix bugzilla: https://gitee.com/src-openeuler/kernel/issues/I99JXH CVE: CVE-2023-52617 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit?id=dec529b0b0572b32f9eb91c882dd1f08ca657efb -------------------------------- The commit in Fixes changed the logic on how resources are released and introduced a new switchtec_exit_pci() that need to be called explicitly in order to undo a corresponding switchtec_init_pci(). This was done in the remove function, but not in the probe. Fix the probe now. Fixes: df25461119d9 ("PCI: switchtec: Fix stdev_release() crash after surprise hot remove") Link: https://lore.kernel.org/r/01446d2ccb91a578239915812f2b7dfbeb2882af.1703428183.git.christophe.jaillet@wanadoo.fr Signed-off-by: Christophe JAILLET Signed-off-by: Bjorn Helgaas Signed-off-by: Xiongfeng Wang --- drivers/pci/switch/switchtec.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/pci/switch/switchtec.c b/drivers/pci/switch/switchtec.c index 5cea3ad290c5..9aa230c3208a 100644 --- a/drivers/pci/switch/switchtec.c +++ b/drivers/pci/switch/switchtec.c @@ -1615,7 +1615,7 @@ static int switchtec_pci_probe(struct pci_dev *pdev, rc = switchtec_init_isr(stdev); if (rc) { dev_err(&stdev->dev, "failed to init isr.\n"); - goto err_put; + goto err_exit_pci; } iowrite32(SWITCHTEC_EVENT_CLEAR | @@ -1636,6 +1636,8 @@ static int switchtec_pci_probe(struct pci_dev *pdev, err_devadd: stdev_kill(stdev); +err_exit_pci: + switchtec_exit_pci(stdev); err_put: ida_simple_remove(&switchtec_minor_ida, MINOR(stdev->dev.devt)); put_device(&stdev->dev); -- Gitee