ALSA: line6: use dynamic buffers

commit e5c812e84f upstream.

The line6 driver uses a lot of USB buffers off of the stack, which is
not allowed on many systems, causing the driver to crash on some of
them.  Fix this up by dynamically allocating the buffers with kmalloc()
which allows for proper DMA-able memory.

Reported-by: Christo Gouws <gouws.christo@gmail.com>
Reported-by: Alan Stern <stern@rowland.harvard.edu>
Tested-by: Christo Gouws <gouws.christo@gmail.com>
Cc: stable <stable@vger.kernel.org>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Greg Kroah-Hartman 2019-04-28 18:04:11 +02:00
parent 68d49ff477
commit d0a5000f10
3 changed files with 65 additions and 40 deletions

View File

@ -351,12 +351,16 @@ int line6_read_data(struct usb_line6 *line6, unsigned address, void *data,
{ {
struct usb_device *usbdev = line6->usbdev; struct usb_device *usbdev = line6->usbdev;
int ret; int ret;
unsigned char len; unsigned char *len;
unsigned count; unsigned count;
if (address > 0xffff || datalen > 0xff) if (address > 0xffff || datalen > 0xff)
return -EINVAL; return -EINVAL;
len = kmalloc(sizeof(*len), GFP_KERNEL);
if (!len)
return -ENOMEM;
/* query the serial number: */ /* query the serial number: */
ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), 0x67, ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), 0x67,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
@ -365,7 +369,7 @@ int line6_read_data(struct usb_line6 *line6, unsigned address, void *data,
if (ret < 0) { if (ret < 0) {
dev_err(line6->ifcdev, "read request failed (error %d)\n", ret); dev_err(line6->ifcdev, "read request failed (error %d)\n", ret);
return ret; goto exit;
} }
/* Wait for data length. We'll get 0xff until length arrives. */ /* Wait for data length. We'll get 0xff until length arrives. */
@ -375,28 +379,29 @@ int line6_read_data(struct usb_line6 *line6, unsigned address, void *data,
ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), 0x67, ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), 0x67,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_TYPE_VENDOR | USB_RECIP_DEVICE |
USB_DIR_IN, USB_DIR_IN,
0x0012, 0x0000, &len, 1, 0x0012, 0x0000, len, 1,
LINE6_TIMEOUT * HZ); LINE6_TIMEOUT * HZ);
if (ret < 0) { if (ret < 0) {
dev_err(line6->ifcdev, dev_err(line6->ifcdev,
"receive length failed (error %d)\n", ret); "receive length failed (error %d)\n", ret);
return ret; goto exit;
} }
if (len != 0xff) if (*len != 0xff)
break; break;
} }
if (len == 0xff) { ret = -EIO;
if (*len == 0xff) {
dev_err(line6->ifcdev, "read failed after %d retries\n", dev_err(line6->ifcdev, "read failed after %d retries\n",
count); count);
return -EIO; goto exit;
} else if (len != datalen) { } else if (*len != datalen) {
/* should be equal or something went wrong */ /* should be equal or something went wrong */
dev_err(line6->ifcdev, dev_err(line6->ifcdev,
"length mismatch (expected %d, got %d)\n", "length mismatch (expected %d, got %d)\n",
(int)datalen, (int)len); (int)datalen, (int)*len);
return -EIO; goto exit;
} }
/* receive the result: */ /* receive the result: */
@ -405,12 +410,12 @@ int line6_read_data(struct usb_line6 *line6, unsigned address, void *data,
0x0013, 0x0000, data, datalen, 0x0013, 0x0000, data, datalen,
LINE6_TIMEOUT * HZ); LINE6_TIMEOUT * HZ);
if (ret < 0) { if (ret < 0)
dev_err(line6->ifcdev, "read failed (error %d)\n", ret); dev_err(line6->ifcdev, "read failed (error %d)\n", ret);
return ret;
}
return 0; exit:
kfree(len);
return ret;
} }
EXPORT_SYMBOL_GPL(line6_read_data); EXPORT_SYMBOL_GPL(line6_read_data);
@ -422,12 +427,16 @@ int line6_write_data(struct usb_line6 *line6, unsigned address, void *data,
{ {
struct usb_device *usbdev = line6->usbdev; struct usb_device *usbdev = line6->usbdev;
int ret; int ret;
unsigned char status; unsigned char *status;
int count; int count;
if (address > 0xffff || datalen > 0xffff) if (address > 0xffff || datalen > 0xffff)
return -EINVAL; return -EINVAL;
status = kmalloc(sizeof(*status), GFP_KERNEL);
if (!status)
return -ENOMEM;
ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), 0x67, ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), 0x67,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
0x0022, address, data, datalen, 0x0022, address, data, datalen,
@ -436,7 +445,7 @@ int line6_write_data(struct usb_line6 *line6, unsigned address, void *data,
if (ret < 0) { if (ret < 0) {
dev_err(line6->ifcdev, dev_err(line6->ifcdev,
"write request failed (error %d)\n", ret); "write request failed (error %d)\n", ret);
return ret; goto exit;
} }
for (count = 0; count < LINE6_READ_WRITE_MAX_RETRIES; count++) { for (count = 0; count < LINE6_READ_WRITE_MAX_RETRIES; count++) {
@ -447,28 +456,29 @@ int line6_write_data(struct usb_line6 *line6, unsigned address, void *data,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_TYPE_VENDOR | USB_RECIP_DEVICE |
USB_DIR_IN, USB_DIR_IN,
0x0012, 0x0000, 0x0012, 0x0000,
&status, 1, LINE6_TIMEOUT * HZ); status, 1, LINE6_TIMEOUT * HZ);
if (ret < 0) { if (ret < 0) {
dev_err(line6->ifcdev, dev_err(line6->ifcdev,
"receiving status failed (error %d)\n", ret); "receiving status failed (error %d)\n", ret);
return ret; goto exit;
} }
if (status != 0xff) if (*status != 0xff)
break; break;
} }
if (status == 0xff) { if (*status == 0xff) {
dev_err(line6->ifcdev, "write failed after %d retries\n", dev_err(line6->ifcdev, "write failed after %d retries\n",
count); count);
return -EIO; ret = -EIO;
} else if (status != 0) { } else if (*status != 0) {
dev_err(line6->ifcdev, "write failed (error %d)\n", ret); dev_err(line6->ifcdev, "write failed (error %d)\n", ret);
return -EIO; ret = -EIO;
} }
exit:
return 0; kfree(status);
return ret;
} }
EXPORT_SYMBOL_GPL(line6_write_data); EXPORT_SYMBOL_GPL(line6_write_data);

View File

@ -225,28 +225,32 @@ static void podhd_startup_start_workqueue(struct timer_list *t)
static int podhd_dev_start(struct usb_line6_podhd *pod) static int podhd_dev_start(struct usb_line6_podhd *pod)
{ {
int ret; int ret;
u8 init_bytes[8]; u8 *init_bytes;
int i; int i;
struct usb_device *usbdev = pod->line6.usbdev; struct usb_device *usbdev = pod->line6.usbdev;
init_bytes = kmalloc(8, GFP_KERNEL);
if (!init_bytes)
return -ENOMEM;
ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0),
0x67, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, 0x67, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
0x11, 0, 0x11, 0,
NULL, 0, LINE6_TIMEOUT * HZ); NULL, 0, LINE6_TIMEOUT * HZ);
if (ret < 0) { if (ret < 0) {
dev_err(pod->line6.ifcdev, "read request failed (error %d)\n", ret); dev_err(pod->line6.ifcdev, "read request failed (error %d)\n", ret);
return ret; goto exit;
} }
/* NOTE: looks like some kind of ping message */ /* NOTE: looks like some kind of ping message */
ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), 0x67, ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), 0x67,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
0x11, 0x0, 0x11, 0x0,
&init_bytes, 3, LINE6_TIMEOUT * HZ); init_bytes, 3, LINE6_TIMEOUT * HZ);
if (ret < 0) { if (ret < 0) {
dev_err(pod->line6.ifcdev, dev_err(pod->line6.ifcdev,
"receive length failed (error %d)\n", ret); "receive length failed (error %d)\n", ret);
return ret; goto exit;
} }
pod->firmware_version = pod->firmware_version =
@ -255,7 +259,7 @@ static int podhd_dev_start(struct usb_line6_podhd *pod)
for (i = 0; i <= 16; i++) { for (i = 0; i <= 16; i++) {
ret = line6_read_data(&pod->line6, 0xf000 + 0x08 * i, init_bytes, 8); ret = line6_read_data(&pod->line6, 0xf000 + 0x08 * i, init_bytes, 8);
if (ret < 0) if (ret < 0)
return ret; goto exit;
} }
ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0),
@ -263,10 +267,9 @@ static int podhd_dev_start(struct usb_line6_podhd *pod)
USB_TYPE_STANDARD | USB_RECIP_DEVICE | USB_DIR_OUT, USB_TYPE_STANDARD | USB_RECIP_DEVICE | USB_DIR_OUT,
1, 0, 1, 0,
NULL, 0, LINE6_TIMEOUT * HZ); NULL, 0, LINE6_TIMEOUT * HZ);
if (ret < 0) exit:
return ret; kfree(init_bytes);
return ret;
return 0;
} }
static void podhd_startup_workqueue(struct work_struct *work) static void podhd_startup_workqueue(struct work_struct *work)

View File

@ -365,16 +365,21 @@ static bool toneport_has_source_select(struct usb_line6_toneport *toneport)
/* /*
Setup Toneport device. Setup Toneport device.
*/ */
static void toneport_setup(struct usb_line6_toneport *toneport) static int toneport_setup(struct usb_line6_toneport *toneport)
{ {
u32 ticks; u32 *ticks;
struct usb_line6 *line6 = &toneport->line6; struct usb_line6 *line6 = &toneport->line6;
struct usb_device *usbdev = line6->usbdev; struct usb_device *usbdev = line6->usbdev;
ticks = kmalloc(sizeof(*ticks), GFP_KERNEL);
if (!ticks)
return -ENOMEM;
/* sync time on device with host: */ /* sync time on device with host: */
/* note: 32-bit timestamps overflow in year 2106 */ /* note: 32-bit timestamps overflow in year 2106 */
ticks = (u32)ktime_get_real_seconds(); *ticks = (u32)ktime_get_real_seconds();
line6_write_data(line6, 0x80c6, &ticks, 4); line6_write_data(line6, 0x80c6, ticks, 4);
kfree(ticks);
/* enable device: */ /* enable device: */
toneport_send_cmd(usbdev, 0x0301, 0x0000); toneport_send_cmd(usbdev, 0x0301, 0x0000);
@ -389,6 +394,7 @@ static void toneport_setup(struct usb_line6_toneport *toneport)
toneport_update_led(toneport); toneport_update_led(toneport);
mod_timer(&toneport->timer, jiffies + TONEPORT_PCM_DELAY * HZ); mod_timer(&toneport->timer, jiffies + TONEPORT_PCM_DELAY * HZ);
return 0;
} }
/* /*
@ -451,7 +457,9 @@ static int toneport_init(struct usb_line6 *line6,
return err; return err;
} }
toneport_setup(toneport); err = toneport_setup(toneport);
if (err)
return err;
/* register audio system: */ /* register audio system: */
return snd_card_register(line6->card); return snd_card_register(line6->card);
@ -463,7 +471,11 @@ static int toneport_init(struct usb_line6 *line6,
*/ */
static int toneport_reset_resume(struct usb_interface *interface) static int toneport_reset_resume(struct usb_interface *interface)
{ {
toneport_setup(usb_get_intfdata(interface)); int err;
err = toneport_setup(usb_get_intfdata(interface));
if (err)
return err;
return line6_resume(interface); return line6_resume(interface);
} }
#endif #endif