Skip to content

Commit

Permalink
rtl8187: Use usb anchor facilities to manage urbs
Browse files Browse the repository at this point in the history
When SLUB debugging is enabled in the kernel, and the boot command includes
the option "slub_debug=P", rtl8187 encounters a GPF due to a read-after-free
of a urb.

Following the example of changes in p54usb to fix the same problem, the code
has been modified to use the usb_anchor_urb() method. With this change, the
USB core handles the freeing of urb's.

This patch fixes the problem reported in Kernel Bugzilla #12185
(http://bugzilla.kernel.org/show_bug.cgi?id=12185).

Signed-off-by: Larry Finger <Larry.Finger@lwfinger.net>
Tested-by: Hin-Tak Leung <htl10@users.sourceforge.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
  • Loading branch information
Larry Finger authored and John W. Linville committed Dec 12, 2008
1 parent 388cdf3 commit c1db52b
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 25 deletions.
2 changes: 1 addition & 1 deletion drivers/net/wireless/rtl818x/rtl8187.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ struct rtl8187_priv {
struct ieee80211_supported_band band;
struct usb_device *udev;
u32 rx_conf;
struct usb_anchor anchored;
u16 txpwr_base;
u8 asic_rev;
u8 is_rtl8187b;
Expand All @@ -115,7 +116,6 @@ struct rtl8187_priv {
u8 aifsn[4];
struct {
__le64 buf;
struct urb *urb;
struct sk_buff_head queue;
} b_tx_status;
};
Expand Down
75 changes: 51 additions & 24 deletions drivers/net/wireless/rtl818x/rtl8187_dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ static const struct ieee80211_channel rtl818x_channels[] = {
static void rtl8187_iowrite_async_cb(struct urb *urb)
{
kfree(urb->context);
usb_free_urb(urb);
}

static void rtl8187_iowrite_async(struct rtl8187_priv *priv, __le16 addr,
Expand Down Expand Up @@ -136,11 +135,13 @@ static void rtl8187_iowrite_async(struct rtl8187_priv *priv, __le16 addr,
usb_fill_control_urb(urb, priv->udev, usb_sndctrlpipe(priv->udev, 0),
(unsigned char *)dr, buf, len,
rtl8187_iowrite_async_cb, buf);
usb_anchor_urb(urb, &priv->anchored);
rc = usb_submit_urb(urb, GFP_ATOMIC);
if (rc < 0) {
kfree(buf);
usb_free_urb(urb);
usb_unanchor_urb(urb);
}
usb_free_urb(urb);
}

static inline void rtl818x_iowrite32_async(struct rtl8187_priv *priv,
Expand Down Expand Up @@ -172,7 +173,6 @@ static void rtl8187_tx_cb(struct urb *urb)
struct ieee80211_hw *hw = info->rate_driver_data[0];
struct rtl8187_priv *priv = hw->priv;

usb_free_urb(info->rate_driver_data[1]);
skb_pull(skb, priv->is_rtl8187b ? sizeof(struct rtl8187b_tx_hdr) :
sizeof(struct rtl8187_tx_hdr));
ieee80211_tx_info_clear_status(info);
Expand Down Expand Up @@ -273,11 +273,13 @@ static int rtl8187_tx(struct ieee80211_hw *dev, struct sk_buff *skb)

usb_fill_bulk_urb(urb, priv->udev, usb_sndbulkpipe(priv->udev, ep),
buf, skb->len, rtl8187_tx_cb, skb);
usb_anchor_urb(urb, &priv->anchored);
rc = usb_submit_urb(urb, GFP_ATOMIC);
if (rc < 0) {
usb_free_urb(urb);
usb_unanchor_urb(urb);
kfree_skb(skb);
}
usb_free_urb(urb);

return 0;
}
Expand All @@ -301,14 +303,13 @@ static void rtl8187_rx_cb(struct urb *urb)
return;
}
spin_unlock(&priv->rx_queue.lock);
skb_put(skb, urb->actual_length);

if (unlikely(urb->status)) {
usb_free_urb(urb);
dev_kfree_skb_irq(skb);
return;
}

skb_put(skb, urb->actual_length);
if (!priv->is_rtl8187b) {
struct rtl8187_rx_hdr *hdr =
(typeof(hdr))(skb_tail_pointer(skb) - sizeof(*hdr));
Expand Down Expand Up @@ -361,7 +362,6 @@ static void rtl8187_rx_cb(struct urb *urb)

skb = dev_alloc_skb(RTL8187_MAX_RX);
if (unlikely(!skb)) {
usb_free_urb(urb);
/* TODO check rx queue length and refill *somewhere* */
return;
}
Expand All @@ -373,24 +373,32 @@ static void rtl8187_rx_cb(struct urb *urb)
urb->context = skb;
skb_queue_tail(&priv->rx_queue, skb);

usb_submit_urb(urb, GFP_ATOMIC);
usb_anchor_urb(urb, &priv->anchored);
if (usb_submit_urb(urb, GFP_ATOMIC)) {
usb_unanchor_urb(urb);
skb_unlink(skb, &priv->rx_queue);
dev_kfree_skb_irq(skb);
}
}

static int rtl8187_init_urbs(struct ieee80211_hw *dev)
{
struct rtl8187_priv *priv = dev->priv;
struct urb *entry;
struct urb *entry = NULL;
struct sk_buff *skb;
struct rtl8187_rx_info *info;
int ret = 0;

while (skb_queue_len(&priv->rx_queue) < 8) {
skb = __dev_alloc_skb(RTL8187_MAX_RX, GFP_KERNEL);
if (!skb)
break;
if (!skb) {
ret = -ENOMEM;
goto err;
}
entry = usb_alloc_urb(0, GFP_KERNEL);
if (!entry) {
kfree_skb(skb);
break;
ret = -ENOMEM;
goto err;
}
usb_fill_bulk_urb(entry, priv->udev,
usb_rcvbulkpipe(priv->udev,
Expand All @@ -401,10 +409,22 @@ static int rtl8187_init_urbs(struct ieee80211_hw *dev)
info->urb = entry;
info->dev = dev;
skb_queue_tail(&priv->rx_queue, skb);
usb_submit_urb(entry, GFP_KERNEL);
usb_anchor_urb(entry, &priv->anchored);
ret = usb_submit_urb(entry, GFP_KERNEL);
if (ret) {
skb_unlink(skb, &priv->rx_queue);
usb_unanchor_urb(entry);
goto err;
}
usb_free_urb(entry);
}
return ret;

return 0;
err:
usb_free_urb(entry);
kfree_skb(skb);
usb_kill_anchored_urbs(&priv->anchored);
return ret;
}

static void rtl8187b_status_cb(struct urb *urb)
Expand All @@ -414,10 +434,8 @@ static void rtl8187b_status_cb(struct urb *urb)
u64 val;
unsigned int cmd_type;

if (unlikely(urb->status)) {
usb_free_urb(urb);
if (unlikely(urb->status))
return;
}

/*
* Read from status buffer:
Expand Down Expand Up @@ -488,26 +506,32 @@ static void rtl8187b_status_cb(struct urb *urb)
spin_unlock_irqrestore(&priv->b_tx_status.queue.lock, flags);
}

usb_submit_urb(urb, GFP_ATOMIC);
usb_anchor_urb(urb, &priv->anchored);
if (usb_submit_urb(urb, GFP_ATOMIC))
usb_unanchor_urb(urb);
}

static int rtl8187b_init_status_urb(struct ieee80211_hw *dev)
{
struct rtl8187_priv *priv = dev->priv;
struct urb *entry;
int ret = 0;

entry = usb_alloc_urb(0, GFP_KERNEL);
if (!entry)
return -ENOMEM;
priv->b_tx_status.urb = entry;

usb_fill_bulk_urb(entry, priv->udev, usb_rcvbulkpipe(priv->udev, 9),
&priv->b_tx_status.buf, sizeof(priv->b_tx_status.buf),
rtl8187b_status_cb, dev);

usb_submit_urb(entry, GFP_KERNEL);
usb_anchor_urb(entry, &priv->anchored);
ret = usb_submit_urb(entry, GFP_KERNEL);
if (ret)
usb_unanchor_urb(entry);
usb_free_urb(entry);

return 0;
return ret;
}

static int rtl8187_cmd_reset(struct ieee80211_hw *dev)
Expand Down Expand Up @@ -841,6 +865,9 @@ static int rtl8187_start(struct ieee80211_hw *dev)
return ret;

mutex_lock(&priv->conf_mutex);

init_usb_anchor(&priv->anchored);

if (priv->is_rtl8187b) {
reg = RTL818X_RX_CONF_MGMT |
RTL818X_RX_CONF_DATA |
Expand Down Expand Up @@ -936,12 +963,12 @@ static void rtl8187_stop(struct ieee80211_hw *dev)

while ((skb = skb_dequeue(&priv->rx_queue))) {
info = (struct rtl8187_rx_info *)skb->cb;
usb_kill_urb(info->urb);
kfree_skb(skb);
}
while ((skb = skb_dequeue(&priv->b_tx_status.queue)))
dev_kfree_skb_any(skb);
usb_kill_urb(priv->b_tx_status.urb);

usb_kill_anchored_urbs(&priv->anchored);
mutex_unlock(&priv->conf_mutex);
}

Expand Down

0 comments on commit c1db52b

Please sign in to comment.