[PATCH 2/3] Bluetooth: btintel_pcie: Fix bounds checks in TX completion handler

ZhaoJinming <[email protected]>
Newsgroups org.kernel.vger.linux-bluetooth,org.kernel.vger.linux-kernel
Message-ID <20260820-btintel_pcie_bounds_fixes-v1-2-c9dcd1ac8bf6@uniontech.com>
Fix two issues in btintel_pcie_msix_tx_handle():

1. cr_tia is a device-controlled value from shared DMA memory
   (data->ia.cr_tia[]) and is used to index txq->urbd0s[] without
   a bounds check. An out-of-range value could cause an out-of-bounds
   access when indexing txq->urbd0s[]. Add a bounds check before the
   array access. When cr_tia is out of range, reset the ring consumer
   pointer (data->ia.cr_tia[]) to cr_hia so the queue can recover on
   the next interrupt.

2. The existing check on urbd0->tfd_index uses '>' instead of '>=',
   allowing tfd_index == txq->count (32) to pass. This check guards
   against a device-controlled value, so the comparison must reject
   all out-of-range indices. Read tfd_index via READ_ONCE() to
   ensure a single atomic read from DMA-coherent memory.

Fixes: c2b636b3f788 ("Bluetooth: btintel_pcie: Add support for PCIe transport")
Signed-off-by: ZhaoJinming <[email protected]>
---
 drivers/bluetooth/btintel_pcie.c | 30 +++++++++++++++++++++++++++---
 1 file changed, 27 insertions(+), 3 deletions(-)

diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c
index fe50c5699e12ef3819577e0f0bd1b79d4340bd9e..c1fd5feb9f81bbd70fabd12b12eb4a1708f6a91f 100644
--- a/drivers/bluetooth/btintel_pcie.c
+++ b/drivers/bluetooth/btintel_pcie.c
@@ -1081,9 +1081,10 @@ static void btintel_pcie_msix_gp0_handler(struct btintel_pcie_data *data)
  */
 static void btintel_pcie_msix_tx_handle(struct btintel_pcie_data *data)
 {
-	u16 cr_tia, cr_hia;
+	u16 cr_tia, cr_hia, tfd_index;
 	struct txq *txq;
 	struct urbd0 *urbd0;
+	struct hci_dev *hdev = data->hdev;
 
 	cr_tia = data->ia.cr_tia[BTINTEL_PCIE_TXQ_NUM];
 	cr_hia = data->ia.cr_hia[BTINTEL_PCIE_TXQ_NUM];
@@ -1094,13 +1095,36 @@ static void btintel_pcie_msix_tx_handle(struct btintel_pcie_data *data)
 	txq = &data->txq;
 
 	while (cr_tia != cr_hia) {
+		if (cr_tia >= txq->count) {
+			bt_dev_err(hdev, "TXQ: invalid cr_tia %u >= %u, contact device vendor",
+				   cr_tia, txq->count);
+			/* Reset consumer pointer so the ring can
+			 * recover on the next interrupt.
+			 */
+			data->ia.cr_tia[BTINTEL_PCIE_TXQ_NUM] = cr_hia;
+			break;
+		}
+
 		data->tx_wait_done = true;
 		wake_up(&data->tx_wait_q);
 
 		urbd0 = &txq->urbd0s[cr_tia];
 
-		if (urbd0->tfd_index > txq->count)
-			return;
+		/* tfd_index is a bitfield in DMA-coherent memory;
+		 * read the full word once with READ_ONCE to avoid
+		 * TOCTOU race with the device.
+		 */
+		tfd_index = READ_ONCE(*(const u32 *)urbd0) & 0xffff;
+
+		if (tfd_index >= txq->count) {
+			bt_dev_err(hdev, "TXQ: invalid tfd_index %u >= %u, contact device vendor",
+				   tfd_index, txq->count);
+			/* Device provided invalid data. Leave cr_tia
+			 * unchanged so the error remains detectable
+			 * via repeated log messages, aiding debug.
+			 */
+			break;
+		}
 
 		cr_tia = (cr_tia + 1) % txq->count;
 		data->ia.cr_tia[BTINTEL_PCIE_TXQ_NUM] = cr_tia;

-- 
2.51.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.