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

Commit c1db52b9 authored by Larry Finger's avatar Larry Finger Committed by John W. Linville
Browse files

rtl8187: Use usb anchor facilities to manage urbs

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: default avatarLarry Finger <Larry.Finger@lwfinger.net>
Tested-by: default avatarHin-Tak Leung <htl10@users.sourceforge.net>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 388cdf31
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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;
@@ -115,7 +116,6 @@ struct rtl8187_priv {
	u8 aifsn[4];
	struct {
		__le64 buf;
		struct urb *urb;
		struct sk_buff_head queue;
	} b_tx_status;
};
+51 −24
Original line number Diff line number Diff line
@@ -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,
@@ -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,
@@ -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);
@@ -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;
}
@@ -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));
@@ -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;
	}
@@ -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,
@@ -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)
@@ -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:
@@ -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)
@@ -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 |
@@ -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);
}