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

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
...@@ -666,7 +666,8 @@ static void _rtl_pci_tx_isr(struct ieee80211_hw *hw, int prio) ...@@ -666,7 +666,8 @@ static void _rtl_pci_tx_isr(struct ieee80211_hw *hw, int prio)
} }
static int _rtl_pci_init_one_rxdesc(struct ieee80211_hw *hw, 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_priv *rtlpriv = rtl_priv(hw);
struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(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, ...@@ -674,11 +675,15 @@ static int _rtl_pci_init_one_rxdesc(struct ieee80211_hw *hw,
u8 tmp_one = 1; u8 tmp_one = 1;
struct sk_buff *skb; struct sk_buff *skb;
if (likely(new_skb)) {
skb = new_skb;
goto remap;
}
skb = dev_alloc_skb(rtlpci->rxbuffersize); skb = dev_alloc_skb(rtlpci->rxbuffersize);
if (!skb) if (!skb)
return 0; 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 */ /* just set skb->cb to mapping addr for pci_unmap_single use */
*((dma_addr_t *)skb->cb) = *((dma_addr_t *)skb->cb) =
pci_map_single(rtlpci->pdev, skb_tail_pointer(skb), pci_map_single(rtlpci->pdev, skb_tail_pointer(skb),
...@@ -686,6 +691,7 @@ static int _rtl_pci_init_one_rxdesc(struct ieee80211_hw *hw, ...@@ -686,6 +691,7 @@ static int _rtl_pci_init_one_rxdesc(struct ieee80211_hw *hw,
bufferaddress = *((dma_addr_t *)skb->cb); bufferaddress = *((dma_addr_t *)skb->cb);
if (pci_dma_mapping_error(rtlpci->pdev, bufferaddress)) if (pci_dma_mapping_error(rtlpci->pdev, bufferaddress))
return 0; return 0;
rtlpci->rx_ring[rxring_idx].rx_buf[desc_idx] = skb;
if (rtlpriv->use_new_trx_flow) { if (rtlpriv->use_new_trx_flow) {
rtlpriv->cfg->ops->set_desc(hw, (u8 *)entry, false, rtlpriv->cfg->ops->set_desc(hw, (u8 *)entry, false,
HW_DESC_RX_PREPARE, HW_DESC_RX_PREPARE,
...@@ -781,6 +787,7 @@ static void _rtl_pci_rx_interrupt(struct ieee80211_hw *hw) ...@@ -781,6 +787,7 @@ static void _rtl_pci_rx_interrupt(struct ieee80211_hw *hw)
/*rx pkt */ /*rx pkt */
struct sk_buff *skb = rtlpci->rx_ring[rxring_idx].rx_buf[ struct sk_buff *skb = rtlpci->rx_ring[rxring_idx].rx_buf[
rtlpci->rx_ring[rxring_idx].idx]; rtlpci->rx_ring[rxring_idx].idx];
struct sk_buff *new_skb;
if (rtlpriv->use_new_trx_flow) { if (rtlpriv->use_new_trx_flow) {
rx_remained_cnt = rx_remained_cnt =
...@@ -807,6 +814,13 @@ static void _rtl_pci_rx_interrupt(struct ieee80211_hw *hw) ...@@ -807,6 +814,13 @@ static void _rtl_pci_rx_interrupt(struct ieee80211_hw *hw)
pci_unmap_single(rtlpci->pdev, *((dma_addr_t *)skb->cb), pci_unmap_single(rtlpci->pdev, *((dma_addr_t *)skb->cb),
rtlpci->rxbuffersize, PCI_DMA_FROMDEVICE); 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) { if (rtlpriv->use_new_trx_flow) {
buffer_desc = buffer_desc =
&rtlpci->rx_ring[rxring_idx].buffer_desc &rtlpci->rx_ring[rxring_idx].buffer_desc
...@@ -911,14 +925,16 @@ static void _rtl_pci_rx_interrupt(struct ieee80211_hw *hw) ...@@ -911,14 +925,16 @@ static void _rtl_pci_rx_interrupt(struct ieee80211_hw *hw)
schedule_work(&rtlpriv->works.lps_change_work); schedule_work(&rtlpriv->works.lps_change_work);
} }
end: end:
skb = new_skb;
no_new:
if (rtlpriv->use_new_trx_flow) { 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, rxring_idx,
rtlpci->rx_ring[rxring_idx].idx); rtlpci->rx_ring[rxring_idx].idx);
} else { } 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); rtlpci->rx_ring[rxring_idx].idx);
if (rtlpci->rx_ring[rxring_idx].idx == if (rtlpci->rx_ring[rxring_idx].idx ==
rtlpci->rxringcount - 1) rtlpci->rxringcount - 1)
rtlpriv->cfg->ops->set_desc(hw, (u8 *)pdesc, 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) ...@@ -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; rtlpci->rx_ring[rxring_idx].idx = 0;
for (i = 0; i < rtlpci->rxringcount; i++) { for (i = 0; i < rtlpci->rxringcount; i++) {
entry = &rtlpci->rx_ring[rxring_idx].buffer_desc[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)) rxring_idx, i))
return -ENOMEM; return -ENOMEM;
} }
...@@ -1332,7 +1348,7 @@ static int _rtl_pci_init_rx_ring(struct ieee80211_hw *hw, int rxring_idx) ...@@ -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++) { for (i = 0; i < rtlpci->rxringcount; i++) {
entry = &rtlpci->rx_ring[rxring_idx].desc[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)) rxring_idx, i))
return -ENOMEM; return -ENOMEM;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment