From 5abc411bb9abef94fd6731e3a99c1457e1595d69 Mon Sep 17 00:00:00 2001 From: Adrian Nicolau Date: Thu, 7 May 2026 10:58:15 +0300 Subject: [PATCH 1/4] tentative fix --- drivers/net/wireless/ath/ath12k/hal.c | 2 +- drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c | 38 ++++++++++++------- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/hal.c b/drivers/net/wireless/ath/ath12k/hal.c index bad672942ee35..686872477fee9 100644 --- a/drivers/net/wireless/ath/ath12k/hal.c +++ b/drivers/net/wireless/ath/ath12k/hal.c @@ -383,7 +383,7 @@ int ath12k_hal_srng_dst_num_free(struct ath12k_base *ab, struct hal_srng *srng, tp = srng->u.dst_ring.tp; if (sync_hw_ptr) { - hp = *srng->u.dst_ring.hp_addr; + hp = READ_ONCE(*srng->u.dst_ring.hp_addr); srng->u.dst_ring.cached_hp = hp; } else { hp = srng->u.dst_ring.cached_hp; diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c index a3fde83cdad3f..94b59c9cd1efc 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c @@ -710,6 +710,31 @@ int ath12k_wifi7_dp_rx_process(struct ath12k_dp *dp, int ring_id, le32_to_cpu(desc->buf_va_lo)); desc_info = (struct ath12k_rx_desc_info *)((unsigned long)desc_va); + /* Validate desc_info before any dereference. A NULL or + * invalid desc_va from a stale/corrupt descriptor would + * cause a wild pointer dereference and corrupt DMA state. + */ + if (unlikely(!desc_info)) { + device_id = hw_links[hw_link_id].device_id; + partner_dp = ath12k_dp_hw_grp_to_dp(dp_hw_grp, device_id); + if (partner_dp) { + desc_info = ath12k_dp_get_rx_desc(partner_dp, cookie); + if (!desc_info) { + ath12k_warn(ab, "Invalid cookie in manual descriptor retrieval: 0x%x\n", + cookie); + continue; + } + } else { + continue; + } + } + + if (unlikely(desc_info->magic != ATH12K_DP_RX_DESC_MAGIC)) { + ath12k_warn(ab, "Check HW CC implementation, magic 0x%x cookie 0x%x\n", + desc_info->magic, cookie); + continue; + } + device_id = hw_links[hw_link_id].device_id; partner_dp = ath12k_dp_hw_grp_to_dp(dp_hw_grp, device_id); if (unlikely(!partner_dp)) { @@ -724,19 +749,6 @@ int ath12k_wifi7_dp_rx_process(struct ath12k_dp *dp, int ring_id, continue; } - /* retry manual desc retrieval */ - if (!desc_info) { - desc_info = ath12k_dp_get_rx_desc(partner_dp, cookie); - if (!desc_info) { - ath12k_warn(partner_dp->ab, "Invalid cookie in manual descriptor retrieval: 0x%x\n", - cookie); - continue; - } - } - - if (desc_info->magic != ATH12K_DP_RX_DESC_MAGIC) - ath12k_warn(ab, "Check HW CC implementation"); - push_reason = le32_get_bits(desc->info0, HAL_REO_DEST_RING_INFO0_PUSH_REASON); if (push_reason != From 21adaab8ece4868a6dca901eaa3a44f2bf1c20c8 Mon Sep 17 00:00:00 2001 From: Adrian Nicolau Date: Fri, 8 May 2026 19:40:53 +0300 Subject: [PATCH 2/4] Revert "tentative fix" This reverts commit 5abc411bb9abef94fd6731e3a99c1457e1595d69. --- drivers/net/wireless/ath/ath12k/hal.c | 2 +- drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c | 38 +++++++------------ 2 files changed, 14 insertions(+), 26 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/hal.c b/drivers/net/wireless/ath/ath12k/hal.c index 686872477fee9..bad672942ee35 100644 --- a/drivers/net/wireless/ath/ath12k/hal.c +++ b/drivers/net/wireless/ath/ath12k/hal.c @@ -383,7 +383,7 @@ int ath12k_hal_srng_dst_num_free(struct ath12k_base *ab, struct hal_srng *srng, tp = srng->u.dst_ring.tp; if (sync_hw_ptr) { - hp = READ_ONCE(*srng->u.dst_ring.hp_addr); + hp = *srng->u.dst_ring.hp_addr; srng->u.dst_ring.cached_hp = hp; } else { hp = srng->u.dst_ring.cached_hp; diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c index 94b59c9cd1efc..a3fde83cdad3f 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c @@ -710,31 +710,6 @@ int ath12k_wifi7_dp_rx_process(struct ath12k_dp *dp, int ring_id, le32_to_cpu(desc->buf_va_lo)); desc_info = (struct ath12k_rx_desc_info *)((unsigned long)desc_va); - /* Validate desc_info before any dereference. A NULL or - * invalid desc_va from a stale/corrupt descriptor would - * cause a wild pointer dereference and corrupt DMA state. - */ - if (unlikely(!desc_info)) { - device_id = hw_links[hw_link_id].device_id; - partner_dp = ath12k_dp_hw_grp_to_dp(dp_hw_grp, device_id); - if (partner_dp) { - desc_info = ath12k_dp_get_rx_desc(partner_dp, cookie); - if (!desc_info) { - ath12k_warn(ab, "Invalid cookie in manual descriptor retrieval: 0x%x\n", - cookie); - continue; - } - } else { - continue; - } - } - - if (unlikely(desc_info->magic != ATH12K_DP_RX_DESC_MAGIC)) { - ath12k_warn(ab, "Check HW CC implementation, magic 0x%x cookie 0x%x\n", - desc_info->magic, cookie); - continue; - } - device_id = hw_links[hw_link_id].device_id; partner_dp = ath12k_dp_hw_grp_to_dp(dp_hw_grp, device_id); if (unlikely(!partner_dp)) { @@ -749,6 +724,19 @@ int ath12k_wifi7_dp_rx_process(struct ath12k_dp *dp, int ring_id, continue; } + /* retry manual desc retrieval */ + if (!desc_info) { + desc_info = ath12k_dp_get_rx_desc(partner_dp, cookie); + if (!desc_info) { + ath12k_warn(partner_dp->ab, "Invalid cookie in manual descriptor retrieval: 0x%x\n", + cookie); + continue; + } + } + + if (desc_info->magic != ATH12K_DP_RX_DESC_MAGIC) + ath12k_warn(ab, "Check HW CC implementation"); + push_reason = le32_get_bits(desc->info0, HAL_REO_DEST_RING_INFO0_PUSH_REASON); if (push_reason != From 400037bc0ddac30af3f5199372c7741b1b3dc53e Mon Sep 17 00:00:00 2001 From: Adrian Nicolau Date: Fri, 8 May 2026 19:41:32 +0300 Subject: [PATCH 3/4] add logs --- drivers/net/wireless/ath/ath12k/ce.c | 35 +++++++++++++++++-- drivers/net/wireless/ath/ath12k/hal.c | 17 +++++++-- drivers/net/wireless/ath/ath12k/htc.c | 22 +++++++++--- drivers/net/wireless/ath/ath12k/mac.c | 3 +- drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c | 22 +++++++----- 5 files changed, 80 insertions(+), 19 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/ce.c b/drivers/net/wireless/ath/ath12k/ce.c index f13b260c5c96e..f635864ed5068 100644 --- a/drivers/net/wireless/ath/ath12k/ce.c +++ b/drivers/net/wireless/ath/ath12k/ce.c @@ -134,8 +134,28 @@ static int ath12k_ce_completed_recv_next(struct ath12k_ce_pipe *pipe, } *nbytes = ath12k_hal_ce_dst_status_get_length(&ab->hal, desc); + if (*nbytes == 0) { + ath12k_warn(ab, "ce pipe %d recv nbytes 0, status desc raw: %08x %08x %08x %08x, srng hp %u tp %u\n", + pipe->pipe_num, + le32_to_cpu(((__le32 *)desc)[0]), + le32_to_cpu(((__le32 *)desc)[1]), + le32_to_cpu(((__le32 *)desc)[2]), + le32_to_cpu(((__le32 *)desc)[3]), + srng->u.dst_ring.cached_hp, + srng->u.dst_ring.tp); + ret = -EIO; + goto err; + } *skb = pipe->dest_ring->skb[sw_index]; + if (unlikely(!*skb)) { + ath12k_warn(ab, "ce pipe %d recv NULL skb at sw_index %u, nbytes %d, srng hp %u tp %u\n", + pipe->pipe_num, sw_index, *nbytes, + srng->u.dst_ring.cached_hp, + srng->u.dst_ring.tp); + ret = -EIO; + goto err; + } pipe->dest_ring->skb[sw_index] = NULL; sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index); @@ -166,14 +186,23 @@ static void ath12k_ce_recv_process_cb(struct ath12k_ce_pipe *pipe) dma_unmap_single(ab->dev, ATH12K_SKB_RXCB(skb)->paddr, max_nbytes, DMA_FROM_DEVICE); - if (unlikely(max_nbytes < nbytes || nbytes == 0)) { - ath12k_warn(ab, "unexpected rx length (nbytes %d, max %d)", - nbytes, max_nbytes); + if (unlikely(max_nbytes < nbytes)) { + ath12k_warn(ab, "rxed more than expected (nbytes %d, max %d) on ce pipe %d\n", + nbytes, max_nbytes, pipe->pipe_num); dev_kfree_skb_any(skb); continue; } skb_put(skb, nbytes); + + if (unlikely(nbytes < sizeof(struct ath12k_htc_hdr))) { + ath12k_warn(ab, "ce pipe %d recv too short frame: nbytes %d (min %zu)\n", + pipe->pipe_num, nbytes, + sizeof(struct ath12k_htc_hdr)); + dev_kfree_skb_any(skb); + continue; + } + __skb_queue_tail(&list, skb); } diff --git a/drivers/net/wireless/ath/ath12k/hal.c b/drivers/net/wireless/ath/ath12k/hal.c index bad672942ee35..a121238ec89ba 100644 --- a/drivers/net/wireless/ath/ath12k/hal.c +++ b/drivers/net/wireless/ath/ath12k/hal.c @@ -536,11 +536,24 @@ void ath12k_hal_srng_access_begin(struct ath12k_base *ab, struct hal_srng *srng) lockdep_assert_held(&srng->lock); if (srng->ring_dir == HAL_SRNG_DIR_SRC) { - srng->u.src_ring.cached_tp = - *(volatile u32 *)srng->u.src_ring.tp_addr; + u32 tp = *(volatile u32 *)srng->u.src_ring.tp_addr; + + if (unlikely(tp >= srng->ring_size)) { + ath12k_warn(ab, "srng %d src tp out of bounds: tp %u ring_size %u\n", + srng->ring_id, tp, srng->ring_size); + return; + } + srng->u.src_ring.cached_tp = tp; } else { hp = READ_ONCE(*srng->u.dst_ring.hp_addr); + if (unlikely(hp >= srng->ring_size)) { + ath12k_warn(ab, "srng %d dst hp out of bounds: hp %u ring_size %u (raw hp_addr value 0x%08x)\n", + srng->ring_id, hp, srng->ring_size, + READ_ONCE(*srng->u.dst_ring.hp_addr)); + return; + } + if (hp != srng->u.dst_ring.cached_hp) { srng->u.dst_ring.cached_hp = hp; /* Make sure descriptor is read after the head diff --git a/drivers/net/wireless/ath/ath12k/htc.c b/drivers/net/wireless/ath/ath12k/htc.c index 92138caa2a82f..b80bf7547a050 100644 --- a/drivers/net/wireless/ath/ath12k/htc.c +++ b/drivers/net/wireless/ath/ath12k/htc.c @@ -268,7 +268,12 @@ void ath12k_htc_rx_completion_handler(struct ath12k_base *ab, eid = le32_get_bits(hdr->htc_info, HTC_HDR_ENDPOINTID); if (eid >= ATH12K_HTC_EP_COUNT) { - ath12k_warn(ab, "HTC Rx: invalid eid %d\n", eid); + ath12k_warn(ab, "HTC Rx: invalid eid %d, htc_info 0x%08x ctrl_info 0x%08x skb->len %d\n", + eid, le32_to_cpu(hdr->htc_info), + le32_to_cpu(hdr->ctrl_info), skb->len); + ath12k_warn(ab, "HTC Rx: payload after hdr (%d bytes): %*ph\n", + min_t(int, skb->len, 32), + min_t(int, skb->len, 32), skb->data); goto out; } @@ -277,14 +282,21 @@ void ath12k_htc_rx_completion_handler(struct ath12k_base *ab, payload_len = le32_get_bits(hdr->htc_info, HTC_HDR_PAYLOADLEN); if (payload_len + sizeof(*hdr) > ATH12K_HTC_MAX_LEN) { - ath12k_warn(ab, "HTC rx frame too long, len: %zu\n", - payload_len + sizeof(*hdr)); + ath12k_warn(ab, "HTC rx frame too long, len: %zu, htc_info 0x%08x ctrl_info 0x%08x\n", + payload_len + sizeof(*hdr), + le32_to_cpu(hdr->htc_info), + le32_to_cpu(hdr->ctrl_info)); + ath12k_warn(ab, "HTC Rx: payload after hdr (%d bytes): %*ph\n", + min_t(int, skb->len, 32), + min_t(int, skb->len, 32), skb->data); goto out; } if (skb->len < payload_len) { - ath12k_warn(ab, "HTC Rx: insufficient length, got %d, expected %d\n", - skb->len, payload_len); + ath12k_warn(ab, "HTC Rx: insufficient length, got %d, expected %d, htc_info 0x%08x ctrl_info 0x%08x\n", + skb->len, payload_len, + le32_to_cpu(hdr->htc_info), + le32_to_cpu(hdr->ctrl_info)); goto out; } diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 48e68045c14c6..c51325a6f256c 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -5377,7 +5377,8 @@ int ath12k_mac_get_fw_stats(struct ath12k *ar, time_left = wait_for_completion_timeout(&ar->fw_stats_complete, 1 * HZ); if (!time_left) { - ath12k_warn(ab, "time out while waiting for get fw stats\n"); + ath12k_warn(ab, "time out while waiting for get fw stats (pdev_id %d vdev_id %d stats_id 0x%x)\n", + param->pdev_id, param->vdev_id, param->stats_id); return -ETIMEDOUT; } diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c index a3fde83cdad3f..ddc8ec64748dc 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c @@ -513,7 +513,9 @@ static int ath12k_wifi7_dp_rx_process_msdu(struct ath12k_pdev_dp *dp_pdev, ath12k_dp_extract_rx_desc_data(hal, rx_info, rx_desc, lrx_desc); if (!rx_info->msdu_done) { - ath12k_warn(dp->ab, "msdu_done bit in msdu_end is not set\n"); + ath12k_warn(dp->ab, "msdu_done bit in msdu_end is not set, rx_desc first 32 bytes: %*ph\n", + min_t(int, 32, (int)sizeof(*rx_desc)), + rx_desc); ret = -EIO; goto free_out; } @@ -528,9 +530,10 @@ static int ath12k_wifi7_dp_rx_process_msdu(struct ath12k_pdev_dp *dp_pdev, } else if (!rxcb->is_continuation) { if ((msdu_len + hal_rx_desc_sz) > DP_RX_BUFFER_SIZE) { ret = -EINVAL; - ath12k_warn(dp->ab, "invalid msdu len %u\n", msdu_len); - ath12k_dbg_dump(dp->ab, ATH12K_DBG_DATA, NULL, "", rx_desc, - sizeof(*rx_desc)); + ath12k_warn(dp->ab, "invalid msdu len %u (desc_sz %u buf_sz %d), rx_desc first 32 bytes: %*ph\n", + msdu_len, hal_rx_desc_sz, DP_RX_BUFFER_SIZE, + min_t(int, 32, (int)sizeof(*rx_desc)), + rx_desc); goto free_out; } skb_put(msdu, hal_rx_desc_sz + l3_pad_bytes + msdu_len); @@ -1381,9 +1384,10 @@ ath12k_wifi7_dp_process_rx_err_buf(struct ath12k_pdev_dp *dp_pdev, msdu_len = rx_info.msdu_len; if ((msdu_len + hal_rx_desc_sz) > DP_RX_BUFFER_SIZE) { - ath12k_warn(dp->ab, "invalid msdu leng %u", msdu_len); - ath12k_dbg_dump(dp->ab, ATH12K_DBG_DATA, NULL, "", rx_desc, - sizeof(*rx_desc)); + ath12k_warn(dp->ab, "invalid msdu leng %u (desc_sz %u buf_sz %d), rx_desc first 32 bytes: %*ph\n", + msdu_len, hal_rx_desc_sz, DP_RX_BUFFER_SIZE, + min_t(int, 32, (int)sizeof(*rx_desc)), + rx_desc); dev_kfree_skb_any(msdu); goto exit; } @@ -1663,7 +1667,9 @@ static int ath12k_wifi7_dp_rx_h_null_q_desc(struct ath12k_pdev_dp *dp_pdev, if (!rx_info->msdu_done) { ath12k_warn(ab, - "msdu_done bit not set in null_q_des processing\n"); + "msdu_done bit not set in null_q_des processing, rx_desc first 32 bytes: %*ph\n", + min_t(int, 32, hal_rx_desc_sz), + msdu->data); __skb_queue_purge(msdu_list); return -EIO; } From da18ab428600076642217fde57c60f95c83993ed Mon Sep 17 00:00:00 2001 From: Adrian Nicolau Date: Mon, 11 May 2026 09:41:57 +0300 Subject: [PATCH 4/4] do not change logic --- drivers/net/wireless/ath/ath12k/ce.c | 17 ----------------- drivers/net/wireless/ath/ath12k/hal.c | 16 ++++++---------- 2 files changed, 6 insertions(+), 27 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/ce.c b/drivers/net/wireless/ath/ath12k/ce.c index f635864ed5068..a0c056d9aba1c 100644 --- a/drivers/net/wireless/ath/ath12k/ce.c +++ b/drivers/net/wireless/ath/ath12k/ce.c @@ -148,14 +148,6 @@ static int ath12k_ce_completed_recv_next(struct ath12k_ce_pipe *pipe, } *skb = pipe->dest_ring->skb[sw_index]; - if (unlikely(!*skb)) { - ath12k_warn(ab, "ce pipe %d recv NULL skb at sw_index %u, nbytes %d, srng hp %u tp %u\n", - pipe->pipe_num, sw_index, *nbytes, - srng->u.dst_ring.cached_hp, - srng->u.dst_ring.tp); - ret = -EIO; - goto err; - } pipe->dest_ring->skb[sw_index] = NULL; sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index); @@ -194,15 +186,6 @@ static void ath12k_ce_recv_process_cb(struct ath12k_ce_pipe *pipe) } skb_put(skb, nbytes); - - if (unlikely(nbytes < sizeof(struct ath12k_htc_hdr))) { - ath12k_warn(ab, "ce pipe %d recv too short frame: nbytes %d (min %zu)\n", - pipe->pipe_num, nbytes, - sizeof(struct ath12k_htc_hdr)); - dev_kfree_skb_any(skb); - continue; - } - __skb_queue_tail(&list, skb); } diff --git a/drivers/net/wireless/ath/ath12k/hal.c b/drivers/net/wireless/ath/ath12k/hal.c index a121238ec89ba..e531f7d118554 100644 --- a/drivers/net/wireless/ath/ath12k/hal.c +++ b/drivers/net/wireless/ath/ath12k/hal.c @@ -536,23 +536,19 @@ void ath12k_hal_srng_access_begin(struct ath12k_base *ab, struct hal_srng *srng) lockdep_assert_held(&srng->lock); if (srng->ring_dir == HAL_SRNG_DIR_SRC) { - u32 tp = *(volatile u32 *)srng->u.src_ring.tp_addr; - - if (unlikely(tp >= srng->ring_size)) { + srng->u.src_ring.cached_tp = + *(volatile u32 *)srng->u.src_ring.tp_addr; + if (unlikely(srng->u.src_ring.cached_tp >= srng->ring_size)) ath12k_warn(ab, "srng %d src tp out of bounds: tp %u ring_size %u\n", - srng->ring_id, tp, srng->ring_size); - return; - } - srng->u.src_ring.cached_tp = tp; + srng->ring_id, srng->u.src_ring.cached_tp, + srng->ring_size); } else { hp = READ_ONCE(*srng->u.dst_ring.hp_addr); - if (unlikely(hp >= srng->ring_size)) { + if (unlikely(hp >= srng->ring_size)) ath12k_warn(ab, "srng %d dst hp out of bounds: hp %u ring_size %u (raw hp_addr value 0x%08x)\n", srng->ring_id, hp, srng->ring_size, READ_ONCE(*srng->u.dst_ring.hp_addr)); - return; - } if (hp != srng->u.dst_ring.cached_hp) { srng->u.dst_ring.cached_hp = hp;