From: Zhao Dongdong <zhaodongdong@kylinos.cn>
In btintel_pcie_probe(), when btintel_pcie_alloc() succeeds but a
later step (btintel_pcie_enable_bt, btintel_pcie_start_rx, or
btintel_pcie_setup_hdev) fails, the DMA buffers and descriptors
allocated by btintel_pcie_alloc() are not freed. The exit_error
path only resets the device and destroys workqueues, causing a
DMA memory leak.
Fix this by calling btintel_pcie_free(data) after btintel_pcie_reset_bt()
to release all DMA buffers and the DMA pool. A NULL guard is added
in btintel_pcie_free() and data->dma_pool is cleared after any
partial allocation failure in btintel_pcie_alloc(), making the
unconditional free() call safe on all error paths.
Fixes: c2b636b3f788 ("Bluetooth: btintel_pcie: Add support for PCIe transport")
Signed-off-by: Zhao Dongdong <zhaodongdong@kylinos.cn>
---
v2: Fix UAF in probe error path by resetting hardware before freeing DMA.
v1: https://lore.kernel.org/linux-bluetooth/tencent_44ED865C14DA3D3823CA85AE1DF2CEBC1805@qq.com/
---
drivers/bluetooth/btintel_pcie.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c
index 9e39327dc1fe..a005b2e390a2 100644
--- a/drivers/bluetooth/btintel_pcie.c
+++ b/drivers/bluetooth/btintel_pcie.c
@@ -1920,6 +1920,9 @@ static int btintel_pcie_setup_rxq_bufs(struct btintel_pcie_data *data,
static void btintel_pcie_free(struct btintel_pcie_data *data)
{
+ if (!data->dma_pool)
+ return;
+
btintel_pcie_free_rxq_bufs(data, &data->rxq);
btintel_pcie_free_txq_bufs(data, &data->txq);
@@ -1980,6 +1983,7 @@ static int btintel_pcie_alloc(struct btintel_pcie_data *data)
&p_addr);
if (!v_addr) {
dma_pool_destroy(data->dma_pool);
+ data->dma_pool = NULL;
err = -ENOMEM;
goto exit_error;
}
@@ -2072,6 +2076,7 @@ static int btintel_pcie_alloc(struct btintel_pcie_data *data)
exit_error_pool:
dma_pool_free(data->dma_pool, data->dma_v_addr, data->dma_p_addr);
dma_pool_destroy(data->dma_pool);
+ data->dma_pool = NULL;
exit_error:
return err;
}
@@ -2918,6 +2923,7 @@ static int btintel_pcie_probe(struct pci_dev *pdev,
exit_error:
/* reset device before exit */
btintel_pcie_reset_bt(data);
+ btintel_pcie_free(data);
destroy_workqueue(data->coredump_workqueue);
--
2.25.1