[PATCH] ath10k: double check bmi xfer pointers

Michal Kazior michal.kazior at tieto.com
Thu Apr 10 03:05:55 PDT 2014


If for some reason copy engine ring buffer became
corrupt ath10k could crash the machine due to
invalid pointer dereference. It's very unlikely
but devices can never be fully trusted so verify
if the bmi xfer pointer read back from copy engine
matches the original pointer. The bug looked as
follows:

 BUG: unable to handle kernel paging request at ffffffff815d6133
...
 Call Trace:
 [<ffffffff810fdaf4>] ? mark_held_locks+0x71/0x99
 [<ffffffff810c6b6d>] ? __local_bh_enable_ip+0xaa/0xd9
 [<ffffffff810fd7eb>] lock_acquire+0x82/0x9d
 [<ffffffff810f5999>] ? complete+0x19/0x45
 [<ffffffff810c6b72>] ? __local_bh_enable_ip+0xaf/0xd9
 [<ffffffff815d5f9b>] _raw_spin_lock_irqsave+0x47/0x5a
 [<ffffffff810f5999>] ? complete+0x19/0x45
 [<ffffffff810f5999>] complete+0x19/0x45
 [<ffffffffa056d977>] ath10k_pci_hif_exchange_bmi_msg+0x267/0x3f4 [ath10k_pci]
 [<ffffffffa0471b42>] ath10k_hif_exchange_bmi_msg+0xe/0x10 [ath10k_core]
 [<ffffffffa0471f01>] ath10k_bmi_write_memory+0xc4/0x12d [ath10k_core]
 [<ffffffffa046877f>] ath10k_core_start+0x207/0x828 [ath10k_core]
 [<ffffffffa0469723>] ath10k_core_register+0x5ca/0x77f [ath10k_core]
...

Reported-By: Ben Greear <greearb at candelatech.com>
Signed-off-by: Michal Kazior <michal.kazior at tieto.com>
---
 drivers/net/wireless/ath/ath10k/pci.c | 30 ++++++++++++++++++++++--------
 1 file changed, 22 insertions(+), 8 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c
index bf1083d..85e84c9 100644
--- a/drivers/net/wireless/ath/ath10k/pci.c
+++ b/drivers/net/wireless/ath/ath10k/pci.c
@@ -1390,35 +1390,49 @@ err_dma:
 	return ret;
 }
 
-static void ath10k_pci_bmi_send_done(struct ath10k_ce_pipe *ce_state)
+static void ath10k_pci_bmi_send_done(struct ath10k_ce_pipe *ce_state,
+				     struct bmi_xfer *xfer)
 {
-	struct bmi_xfer *xfer;
+	void *ptr;
 	u32 ce_data;
 	unsigned int nbytes;
 	unsigned int transfer_id;
 
-	if (ath10k_ce_completed_send_next(ce_state, (void **)&xfer, &ce_data,
+	if (ath10k_ce_completed_send_next(ce_state, (void **)&ptr, &ce_data,
 					  &nbytes, &transfer_id))
 		return;
 
+	if (xfer != ptr) {
+		ath10k_warn("failed to verify bmi xfer tx pointer (got %p expected %p)\n",
+			    ptr, xfer);
+		return;
+	}
+
 	if (xfer->wait_for_resp)
 		return;
 
 	complete(&xfer->done);
 }
 
-static void ath10k_pci_bmi_recv_data(struct ath10k_ce_pipe *ce_state)
+static void ath10k_pci_bmi_recv_data(struct ath10k_ce_pipe *ce_state,
+				     struct bmi_xfer *xfer)
 {
-	struct bmi_xfer *xfer;
+	void *ptr;
 	u32 ce_data;
 	unsigned int nbytes;
 	unsigned int transfer_id;
 	unsigned int flags;
 
-	if (ath10k_ce_completed_recv_next(ce_state, (void **)&xfer, &ce_data,
+	if (ath10k_ce_completed_recv_next(ce_state, (void **)&ptr, &ce_data,
 					  &nbytes, &transfer_id, &flags))
 		return;
 
+	if (xfer != ptr) {
+		ath10k_warn("failed to verify bmi xfer rx pointer (got %p expected %p)\n",
+			    ptr, xfer);
+		return;
+	}
+
 	if (!xfer->wait_for_resp) {
 		ath10k_warn("unexpected: BMI data received; ignoring\n");
 		return;
@@ -1435,8 +1449,8 @@ static int ath10k_pci_bmi_wait(struct ath10k_ce_pipe *tx_pipe,
 	unsigned long timeout = jiffies + BMI_COMMUNICATION_TIMEOUT_HZ;
 
 	while (time_before_eq(jiffies, timeout)) {
-		ath10k_pci_bmi_send_done(tx_pipe);
-		ath10k_pci_bmi_recv_data(rx_pipe);
+		ath10k_pci_bmi_send_done(tx_pipe, xfer);
+		ath10k_pci_bmi_recv_data(rx_pipe, xfer);
 
 		if (completion_done(&xfer->done))
 			return 0;
-- 
1.8.5.3




More information about the ath10k mailing list