Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 271f1e65 authored by Daniel Mack's avatar Daniel Mack Committed by Kalle Valo
Browse files

wcn36xx: don't keep reference to skb if transmission failed



When wcn36xx_dxe_tx_frame() fails to transmit the TX frame, the driver
will call into ieee80211_free_txskb() for the skb in flight, so it'll no
longer be valid. Hence, we shouldn't keep a reference to it in ctl->skb.
Also, if the skb has IEEE80211_TX_CTL_REQ_TX_STATUS set, a pointer to
it will currently remain in wcn->tx_ack_skb, which will potentially lead
to a crash if accessed later.

Fix this by checking the return value of wcn36xx_dxe_tx_frame(), and
nullify wcn->tx_ack_skb again in case of errors. Move the assignment
of ctl->skb in wcn36xx_dxe_tx_frame() so it only happens when the
transmission is successful.

Signed-off-by: default avatarDaniel Mack <daniel@zonque.org>
Signed-off-by: default avatarKalle Valo <kvalo@codeaurora.org>
parent 7cae3519
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -693,7 +693,6 @@ int wcn36xx_dxe_tx_frame(struct wcn36xx *wcn,

	/* Set source address of the SKB we send */
	ctl = ctl->next;
	ctl->skb = skb;
	desc = ctl->desc;
	if (ctl->bd_cpu_addr) {
		wcn36xx_err("bd_cpu_addr cannot be NULL for skb DXE\n");
@@ -702,8 +701,8 @@ int wcn36xx_dxe_tx_frame(struct wcn36xx *wcn,
	}

	desc->src_addr_l = dma_map_single(wcn->dev,
					  ctl->skb->data,
					  ctl->skb->len,
					  skb->data,
					  skb->len,
					  DMA_TO_DEVICE);
	if (dma_mapping_error(wcn->dev, desc->src_addr_l)) {
		dev_err(wcn->dev, "unable to DMA map src_addr_l\n");
@@ -711,6 +710,7 @@ int wcn36xx_dxe_tx_frame(struct wcn36xx *wcn,
		goto unlock;
	}

	ctl->skb = skb;
	desc->dst_addr_l = ch->dxe_wq;
	desc->fr_len = ctl->skb->len;

+14 −1
Original line number Diff line number Diff line
@@ -273,6 +273,7 @@ int wcn36xx_start_tx(struct wcn36xx *wcn,
	bool bcast = is_broadcast_ether_addr(hdr->addr1) ||
		is_multicast_ether_addr(hdr->addr1);
	struct wcn36xx_tx_bd bd;
	int ret;

	memset(&bd, 0, sizeof(bd));

@@ -317,5 +318,17 @@ int wcn36xx_start_tx(struct wcn36xx *wcn,
	buff_to_be((u32 *)&bd, sizeof(bd)/sizeof(u32));
	bd.tx_bd_sign = 0xbdbdbdbd;

	return wcn36xx_dxe_tx_frame(wcn, vif_priv, &bd, skb, is_low);
	ret = wcn36xx_dxe_tx_frame(wcn, vif_priv, &bd, skb, is_low);
	if (ret && bd.tx_comp) {
		/* If the skb has not been transmitted,
		 * don't keep a reference to it.
		 */
		spin_lock_irqsave(&wcn->dxe_lock, flags);
		wcn->tx_ack_skb = NULL;
		spin_unlock_irqrestore(&wcn->dxe_lock, flags);

		ieee80211_wake_queues(wcn->hw);
	}

	return ret;
}