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

Commit e9538cf4 authored by Larry Finger's avatar Larry Finger Committed by Kalle Valo
Browse files

rtlwifi: Fix error when accessing unmapped memory in skb



These drivers use 9100-byte receive buffers, thus allocating an skb requires
an O(3) memory allocation. Under heavy memory loads and fragmentation, such
a request can fail. Previous versions of the driver have dropped the packet
and reused the old buffer; however, the new version introduced a bug in that
it released the old buffer before trying to allocate a new one. The previous
method is implemented here. The skb is unmapped before any attempt is made to
allocate another.

Signed-off-by: default avatarLarry Finger <Larry.Finger@lwfinger.net>
Cc: Stable <stable@vger.kernel.org>  [v3.18]
Reported-by: default avatarEric Biggers <ebiggers3@gmail.com>
Cc: Eric Biggers <ebiggers3@gmail.com>
Signed-off-by: default avatarKalle Valo <kvalo@codeaurora.org>
parent 7ce67a38
Loading
Loading
Loading
Loading
+24 −8
Original line number Diff line number Diff line
@@ -666,7 +666,8 @@ tx_status_ok:
}

static int _rtl_pci_init_one_rxdesc(struct ieee80211_hw *hw,
				    u8 *entry, int rxring_idx, int desc_idx)
				    struct sk_buff *new_skb, u8 *entry,
				    int rxring_idx, int desc_idx)
{
	struct rtl_priv *rtlpriv = rtl_priv(hw);
	struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
@@ -674,11 +675,15 @@ static int _rtl_pci_init_one_rxdesc(struct ieee80211_hw *hw,
	u8 tmp_one = 1;
	struct sk_buff *skb;

	if (likely(new_skb)) {
		skb = new_skb;
		goto remap;
	}
	skb = dev_alloc_skb(rtlpci->rxbuffersize);
	if (!skb)
		return 0;
	rtlpci->rx_ring[rxring_idx].rx_buf[desc_idx] = skb;

remap:
	/* just set skb->cb to mapping addr for pci_unmap_single use */
	*((dma_addr_t *)skb->cb) =
		pci_map_single(rtlpci->pdev, skb_tail_pointer(skb),
@@ -686,6 +691,7 @@ static int _rtl_pci_init_one_rxdesc(struct ieee80211_hw *hw,
	bufferaddress = *((dma_addr_t *)skb->cb);
	if (pci_dma_mapping_error(rtlpci->pdev, bufferaddress))
		return 0;
	rtlpci->rx_ring[rxring_idx].rx_buf[desc_idx] = skb;
	if (rtlpriv->use_new_trx_flow) {
		rtlpriv->cfg->ops->set_desc(hw, (u8 *)entry, false,
					    HW_DESC_RX_PREPARE,
@@ -781,6 +787,7 @@ static void _rtl_pci_rx_interrupt(struct ieee80211_hw *hw)
		/*rx pkt */
		struct sk_buff *skb = rtlpci->rx_ring[rxring_idx].rx_buf[
				      rtlpci->rx_ring[rxring_idx].idx];
		struct sk_buff *new_skb;

		if (rtlpriv->use_new_trx_flow) {
			rx_remained_cnt =
@@ -807,6 +814,13 @@ static void _rtl_pci_rx_interrupt(struct ieee80211_hw *hw)
		pci_unmap_single(rtlpci->pdev, *((dma_addr_t *)skb->cb),
				 rtlpci->rxbuffersize, PCI_DMA_FROMDEVICE);

		/* get a new skb - if fail, old one will be reused */
		new_skb = dev_alloc_skb(rtlpci->rxbuffersize);
		if (unlikely(!new_skb)) {
			pr_err("Allocation of new skb failed in %s\n",
			       __func__);
			goto no_new;
		}
		if (rtlpriv->use_new_trx_flow) {
			buffer_desc =
			  &rtlpci->rx_ring[rxring_idx].buffer_desc
@@ -911,14 +925,16 @@ static void _rtl_pci_rx_interrupt(struct ieee80211_hw *hw)
			schedule_work(&rtlpriv->works.lps_change_work);
		}
end:
		skb = new_skb;
no_new:
		if (rtlpriv->use_new_trx_flow) {
			_rtl_pci_init_one_rxdesc(hw, (u8 *)buffer_desc,
			_rtl_pci_init_one_rxdesc(hw, skb, (u8 *)buffer_desc,
						 rxring_idx,
						 rtlpci->rx_ring[rxring_idx].idx);
		} else {
			_rtl_pci_init_one_rxdesc(hw, (u8 *)pdesc, rxring_idx,
			_rtl_pci_init_one_rxdesc(hw, skb, (u8 *)pdesc,
						 rxring_idx,
						 rtlpci->rx_ring[rxring_idx].idx);

			if (rtlpci->rx_ring[rxring_idx].idx ==
			    rtlpci->rxringcount - 1)
				rtlpriv->cfg->ops->set_desc(hw, (u8 *)pdesc,
@@ -1307,7 +1323,7 @@ static int _rtl_pci_init_rx_ring(struct ieee80211_hw *hw, int rxring_idx)
		rtlpci->rx_ring[rxring_idx].idx = 0;
		for (i = 0; i < rtlpci->rxringcount; i++) {
			entry = &rtlpci->rx_ring[rxring_idx].buffer_desc[i];
			if (!_rtl_pci_init_one_rxdesc(hw, (u8 *)entry,
			if (!_rtl_pci_init_one_rxdesc(hw, NULL, (u8 *)entry,
						      rxring_idx, i))
				return -ENOMEM;
		}
@@ -1332,7 +1348,7 @@ static int _rtl_pci_init_rx_ring(struct ieee80211_hw *hw, int rxring_idx)

		for (i = 0; i < rtlpci->rxringcount; i++) {
			entry = &rtlpci->rx_ring[rxring_idx].desc[i];
			if (!_rtl_pci_init_one_rxdesc(hw, (u8 *)entry,
			if (!_rtl_pci_init_one_rxdesc(hw, NULL, (u8 *)entry,
						      rxring_idx, i))
				return -ENOMEM;
		}