Commit message: ALSA: usb-audio: avoid freeing umidi object twice

The 'umidi' object will be free'd on the error path by snd_usbmidi_free()
when tearing down the rawmidi interface. So we shouldn't try to free it
in snd_usbmidi_create() after having registered the rawmidi interface.

Found by KASAN.

Signed-off-by: Andrey Konovalov <andreyknvl@gmail.com>
Acked-by: Clemens Ladisch <clemens@ladisch.de>
Cc: <stable@vger.kernel.org>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
CVE: CVE-2016-2384
CWE: CWE-703
Vulnerability description: Double free vulnerability in the snd_usbmidi_create function in sound/usb/midi.c in the Linux kernel before 4.5 allows physically proximate attackers to cause a denial of service (panic) or possibly have unspecified other impact via vectors involving an invalid USB descriptor.

Project: linux
File: midi.c

static void ch345_broken_sysex_input(struct snd_usb_midi_in_endpoint *ep,
				     uint8_t *buffer, int buffer_length)
{
	unsigned int i, cin, length;

	for (i = 0; i + 3 < buffer_length; i += 4) {
		if (buffer[i] == 0 && i > 0)
			break;
		cin = buffer[i] & 0x0f;
		if (ep->in_sysex &&
		    cin == ep->last_cin &&
		    (buffer[i + 1 + (cin == 0x6)] & 0x80) == 0)
			cin = 0x4;
#if 0
		if (buffer[i + 1] == 0x90) {
			/*
			 * Either a corrupted running status or a real note-on
			 * message; impossible to detect reliably.
			 */
		}
#endif
		length = snd_usbmidi_cin_length[cin];
		snd_usbmidi_input_data(ep, 0, &buffer[i + 1], length);
		ep->in_sysex = cin == 0x4;
		if (!ep->in_sysex)
			ep->last_cin = cin;
	}
}

Project: linux
File: midi.c

static void dump_urb(const char *type, const u8 *data, int length)
{
	snd_printk(KERN_DEBUG "%s packet: [", type);
	for (; length > 0; ++data, --length)
		printk(" %02x", *data);
	printk(" ]\n");
}

Project: linux
File: midi.c

void snd_usbmidi_disconnect(struct list_head *p)
{
	struct snd_usb_midi *umidi;
	unsigned int i, j;

	umidi = list_entry(p, struct snd_usb_midi, list);
	/*
	 * an URB's completion handler may start the timer and
	 * a timer may submit an URB. To reliably break the cycle
	 * a flag under lock must be used
	 */
	down_write(&umidi->disc_rwsem);
	spin_lock_irq(&umidi->disc_lock);
	umidi->disconnected = 1;
	spin_unlock_irq(&umidi->disc_lock);
	up_write(&umidi->disc_rwsem);

	for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) {
		struct snd_usb_midi_endpoint *ep = &umidi->endpoints[i];
		if (ep->out)
			tasklet_kill(&ep->out->tasklet);
		if (ep->out) {
			for (j = 0; j < OUTPUT_URBS; ++j)
				usb_kill_urb(ep->out->urbs[j].urb);
			if (umidi->usb_protocol_ops->finish_out_endpoint)
				umidi->usb_protocol_ops->finish_out_endpoint(ep->out);
			ep->out->active_urbs = 0;
			if (ep->out->drain_urbs) {
				ep->out->drain_urbs = 0;
				wake_up(&ep->out->drain_wait);
			}
		}
		if (ep->in)
			for (j = 0; j < INPUT_URBS; ++j)
				usb_kill_urb(ep->in->urbs[j]);
		/* free endpoints here; later call can result in Oops */
		if (ep->out)
			snd_usbmidi_out_endpoint_clear(ep->out);
		if (ep->in) {
			snd_usbmidi_in_endpoint_delete(ep->in);
			ep->in = NULL;
		}
	}
	del_timer_sync(&umidi->error_timer);
}

Project: linux
File: midi.c

static void snd_usbmidi_raw_output(struct snd_usb_midi_out_endpoint *ep,
				   struct urb *urb)
{
	int count;

	if (!ep->ports[0].active)
		return;
	count = snd_rawmidi_transmit(ep->ports[0].substream,
				     urb->transfer_buffer,
				     ep->max_transfer);
	if (count < 1) {
		ep->ports[0].active = 0;
		return;
	}
	urb->transfer_buffer_length = count;
}

Project: linux
File: midi.c

static void snd_usbmidi_switch_roland_altsetting(struct snd_usb_midi *umidi)
{
	struct usb_interface *intf;
	struct usb_host_interface *hostif;
	struct usb_interface_descriptor *intfd;

	intf = umidi->iface;
	if (!intf || intf->num_altsetting != 2)
		return;

	hostif = &intf->altsetting[1];
	intfd = get_iface_desc(hostif);
       /* If either or both of the endpoints support interrupt transfer,
        * then use the alternate setting
        */
	if (intfd->bNumEndpoints != 2 ||
	    !((get_endpoint(hostif, 0)->bmAttributes &
	       USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT ||
	      (get_endpoint(hostif, 1)->bmAttributes &
	       USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT))
		return;

	dev_dbg(&umidi->dev->dev, "switching to altsetting %d with int ep\n",
		    intfd->bAlternateSetting);
	usb_set_interface(umidi->dev, intfd->bInterfaceNumber,
			  intfd->bAlternateSetting);

	umidi->roland_load_ctl = snd_ctl_new1(&roland_load_ctl, umidi);
	if (snd_ctl_add(umidi->card, umidi->roland_load_ctl) < 0)
		umidi->roland_load_ctl = NULL;
}

Project: linux
File: midi.c

static void update_roland_altsetting(struct snd_usb_midi *umidi)
{
	struct usb_interface *intf;
	struct usb_host_interface *hostif;
	struct usb_interface_descriptor *intfd;
	int is_light_load;

	intf = umidi->iface;
	is_light_load = intf->cur_altsetting != intf->altsetting;
	if (umidi->roland_load_ctl->private_value == is_light_load)
		return;
	hostif = &intf->altsetting[umidi->roland_load_ctl->private_value];
	intfd = get_iface_desc(hostif);
	snd_usbmidi_input_stop(&umidi->list);
	usb_set_interface(umidi->dev, intfd->bInterfaceNumber,
			  intfd->bAlternateSetting);
	snd_usbmidi_input_start(&umidi->list);
}