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

Commit bc6b8923 authored by Jussi Kivilinna's avatar Jussi Kivilinna Committed by John W. Linville
Browse files

rtlwifi: usb: allocate URB control message setup_packet and data buffer separately



rtlwifi allocates both setup_packet and data buffer of control message urb,
using shared kmalloc in _usbctrl_vendorreq_async_write. Structure used for
allocating is:
	struct {
		u8 data[254];
		struct usb_ctrlrequest dr;
	};

Because 'struct usb_ctrlrequest' is __packed, setup packet is unaligned and
DMA mapping of both 'data' and 'dr' confuses ARM/sunxi, leading to memory
corruptions and freezes.

Patch changes setup packet to be allocated separately.

[v2]:
 - Use WARN_ON_ONCE instead of WARN_ON

Cc: <stable@vger.kernel.org>
Signed-off-by: default avatarJussi Kivilinna <jussi.kivilinna@mbnet.fi>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent a5f39056
Loading
Loading
Loading
Loading
+28 −16
Original line number Diff line number Diff line
@@ -42,8 +42,12 @@

static void usbctrl_async_callback(struct urb *urb)
{
	if (urb)
		kfree(urb->context);
	if (urb) {
		/* free dr */
		kfree(urb->setup_packet);
		/* free databuf */
		kfree(urb->transfer_buffer);
	}
}

static int _usbctrl_vendorreq_async_write(struct usb_device *udev, u8 request,
@@ -55,39 +59,47 @@ static int _usbctrl_vendorreq_async_write(struct usb_device *udev, u8 request,
	u8 reqtype;
	struct usb_ctrlrequest *dr;
	struct urb *urb;
	struct rtl819x_async_write_data {
		u8 data[REALTEK_USB_VENQT_MAX_BUF_SIZE];
		struct usb_ctrlrequest dr;
	} *buf;
	const u16 databuf_maxlen = REALTEK_USB_VENQT_MAX_BUF_SIZE;
	u8 *databuf;

	if (WARN_ON_ONCE(len > databuf_maxlen))
		len = databuf_maxlen;

	pipe = usb_sndctrlpipe(udev, 0); /* write_out */
	reqtype =  REALTEK_USB_VENQT_WRITE;

	buf = kmalloc(sizeof(*buf), GFP_ATOMIC);
	if (!buf)
	dr = kmalloc(sizeof(*dr), GFP_ATOMIC);
	if (!dr)
		return -ENOMEM;

	databuf = kmalloc(databuf_maxlen, GFP_ATOMIC);
	if (!databuf) {
		kfree(dr);
		return -ENOMEM;
	}

	urb = usb_alloc_urb(0, GFP_ATOMIC);
	if (!urb) {
		kfree(buf);
		kfree(databuf);
		kfree(dr);
		return -ENOMEM;
	}

	dr = &buf->dr;

	dr->bRequestType = reqtype;
	dr->bRequest = request;
	dr->wValue = cpu_to_le16(value);
	dr->wIndex = cpu_to_le16(index);
	dr->wLength = cpu_to_le16(len);
	/* data are already in little-endian order */
	memcpy(buf, pdata, len);
	memcpy(databuf, pdata, len);
	usb_fill_control_urb(urb, udev, pipe,
			     (unsigned char *)dr, buf, len,
			     usbctrl_async_callback, buf);
			     (unsigned char *)dr, databuf, len,
			     usbctrl_async_callback, NULL);
	rc = usb_submit_urb(urb, GFP_ATOMIC);
	if (rc < 0)
		kfree(buf);
	if (rc < 0) {
		kfree(databuf);
		kfree(dr);
	}
	usb_free_urb(urb);
	return rc;
}