aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2014-06-25 08:24:47 -0400
committerTakashi Iwai <tiwai@suse.de>2014-06-26 04:33:35 -0400
commit92a586bdc06de6629dae1b357dac221253f55ff8 (patch)
treee171b5669ae5ebc62c8167e41f9d849382b3bd59 /sound
parent8b3dfdaf0c25a584cb31d04d2574115cf2d422ab (diff)
ALSA: usb-audio: Fix races at disconnection and PCM closing
When a USB-audio device is disconnected while PCM is still running, we still see some race: the disconnect callback calls snd_usb_endpoint_free() that calls release_urbs() and then kfree() while a PCM stream would be closed at the same time and calls stop_endpoints() that leads to wait_clear_urbs(). That is, the EP object might be deallocated while a PCM stream is syncing with wait_clear_urbs() with the same EP. Basically calling multiple wait_clear_urbs() would work fine, also calling wait_clear_urbs() and release_urbs() would work, too, as wait_clear_urbs() just reads some fields in ep. The problem is the succeeding kfree() in snd_pcm_endpoint_free(). This patch moves out the EP deallocation into the later point, the destructor callback. At this stage, all PCMs must have been already closed, so it's safe to free the objects. Reported-by: Alan Stern <stern@rowland.harvard.edu> Cc: <stable@vger.kernel.org> Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound')
-rw-r--r--sound/usb/card.c13
-rw-r--r--sound/usb/endpoint.c17
-rw-r--r--sound/usb/endpoint.h1
3 files changed, 25 insertions, 6 deletions
diff --git a/sound/usb/card.c b/sound/usb/card.c
index c3b5b7dca1c3..a09e5f3519e3 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -307,6 +307,11 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
307 307
308static int snd_usb_audio_free(struct snd_usb_audio *chip) 308static int snd_usb_audio_free(struct snd_usb_audio *chip)
309{ 309{
310 struct list_head *p, *n;
311
312 list_for_each_safe(p, n, &chip->ep_list)
313 snd_usb_endpoint_free(p);
314
310 mutex_destroy(&chip->mutex); 315 mutex_destroy(&chip->mutex);
311 kfree(chip); 316 kfree(chip);
312 return 0; 317 return 0;
@@ -585,7 +590,7 @@ static void snd_usb_audio_disconnect(struct usb_device *dev,
585 struct snd_usb_audio *chip) 590 struct snd_usb_audio *chip)
586{ 591{
587 struct snd_card *card; 592 struct snd_card *card;
588 struct list_head *p, *n; 593 struct list_head *p;
589 594
590 if (chip == (void *)-1L) 595 if (chip == (void *)-1L)
591 return; 596 return;
@@ -598,14 +603,16 @@ static void snd_usb_audio_disconnect(struct usb_device *dev,
598 mutex_lock(&register_mutex); 603 mutex_lock(&register_mutex);
599 chip->num_interfaces--; 604 chip->num_interfaces--;
600 if (chip->num_interfaces <= 0) { 605 if (chip->num_interfaces <= 0) {
606 struct snd_usb_endpoint *ep;
607
601 snd_card_disconnect(card); 608 snd_card_disconnect(card);
602 /* release the pcm resources */ 609 /* release the pcm resources */
603 list_for_each(p, &chip->pcm_list) { 610 list_for_each(p, &chip->pcm_list) {
604 snd_usb_stream_disconnect(p); 611 snd_usb_stream_disconnect(p);
605 } 612 }
606 /* release the endpoint resources */ 613 /* release the endpoint resources */
607 list_for_each_safe(p, n, &chip->ep_list) { 614 list_for_each_entry(ep, &chip->ep_list, list) {
608 snd_usb_endpoint_free(p); 615 snd_usb_endpoint_release(ep);
609 } 616 }
610 /* release the midi resources */ 617 /* release the midi resources */
611 list_for_each(p, &chip->midi_list) { 618 list_for_each(p, &chip->midi_list) {
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index 289f582c9130..114e3e7ff511 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -987,19 +987,30 @@ void snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep)
987} 987}
988 988
989/** 989/**
990 * snd_usb_endpoint_release: Tear down an snd_usb_endpoint
991 *
992 * @ep: the endpoint to release
993 *
994 * This function does not care for the endpoint's use count but will tear
995 * down all the streaming URBs immediately.
996 */
997void snd_usb_endpoint_release(struct snd_usb_endpoint *ep)
998{
999 release_urbs(ep, 1);
1000}
1001
1002/**
990 * snd_usb_endpoint_free: Free the resources of an snd_usb_endpoint 1003 * snd_usb_endpoint_free: Free the resources of an snd_usb_endpoint
991 * 1004 *
992 * @ep: the list header of the endpoint to free 1005 * @ep: the list header of the endpoint to free
993 * 1006 *
994 * This function does not care for the endpoint's use count but will tear 1007 * This free all resources of the given ep.
995 * down all the streaming URBs immediately and free all resources.
996 */ 1008 */
997void snd_usb_endpoint_free(struct list_head *head) 1009void snd_usb_endpoint_free(struct list_head *head)
998{ 1010{
999 struct snd_usb_endpoint *ep; 1011 struct snd_usb_endpoint *ep;
1000 1012
1001 ep = list_entry(head, struct snd_usb_endpoint, list); 1013 ep = list_entry(head, struct snd_usb_endpoint, list);
1002 release_urbs(ep, 1);
1003 kfree(ep); 1014 kfree(ep);
1004} 1015}
1005 1016
diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h
index 1c7e8ee48abc..e61ee5c356a3 100644
--- a/sound/usb/endpoint.h
+++ b/sound/usb/endpoint.h
@@ -23,6 +23,7 @@ void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep);
23void snd_usb_endpoint_sync_pending_stop(struct snd_usb_endpoint *ep); 23void snd_usb_endpoint_sync_pending_stop(struct snd_usb_endpoint *ep);
24int snd_usb_endpoint_activate(struct snd_usb_endpoint *ep); 24int snd_usb_endpoint_activate(struct snd_usb_endpoint *ep);
25void snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep); 25void snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep);
26void snd_usb_endpoint_release(struct snd_usb_endpoint *ep);
26void snd_usb_endpoint_free(struct list_head *head); 27void snd_usb_endpoint_free(struct list_head *head);
27 28
28int snd_usb_endpoint_implicit_feedback_sink(struct snd_usb_endpoint *ep); 29int snd_usb_endpoint_implicit_feedback_sink(struct snd_usb_endpoint *ep);