diff options
author | Vladis Dronov <vdronov@redhat.com> | 2016-03-31 12:05:43 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2016-03-31 12:07:31 -0400 |
commit | 836b34a935abc91e13e63053d0a83b24dfb5ea78 (patch) | |
tree | b2aaeb09188148c679f23eda13f7db4b933a65e9 /sound | |
parent | 8eb22214b7cb0c0a28be6caf3b81201629d8ea7c (diff) |
ALSA: usb-audio: Fix double-free in error paths after snd_usb_add_audio_stream() call
create_fixed_stream_quirk(), snd_usb_parse_audio_interface() and
create_uaxx_quirk() functions allocate the audioformat object by themselves
and free it upon error before returning. However, once the object is linked
to a stream, it's freed again in snd_usb_audio_pcm_free(), thus it'll be
double-freed, eventually resulting in a memory corruption.
This patch fixes these failures in the error paths by unlinking the audioformat
object before freeing it.
Based on a patch by Takashi Iwai <tiwai@suse.de>
[Note for stable backports:
this patch requires the commit 902eb7fd1e4a ('ALSA: usb-audio: Minor
code cleanup in create_fixed_stream_quirk()')]
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1283358
Reported-by: Ralf Spenneberg <ralf@spenneberg.net>
Cc: <stable@vger.kernel.org> # see the note above
Signed-off-by: Vladis Dronov <vdronov@redhat.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound')
-rw-r--r-- | sound/usb/quirks.c | 4 | ||||
-rw-r--r-- | sound/usb/stream.c | 6 |
2 files changed, 9 insertions, 1 deletions
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index fb62bce2435c..6178bb5d0731 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c | |||
@@ -150,6 +150,7 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip, | |||
150 | usb_audio_err(chip, "cannot memdup\n"); | 150 | usb_audio_err(chip, "cannot memdup\n"); |
151 | return -ENOMEM; | 151 | return -ENOMEM; |
152 | } | 152 | } |
153 | INIT_LIST_HEAD(&fp->list); | ||
153 | if (fp->nr_rates > MAX_NR_RATES) { | 154 | if (fp->nr_rates > MAX_NR_RATES) { |
154 | kfree(fp); | 155 | kfree(fp); |
155 | return -EINVAL; | 156 | return -EINVAL; |
@@ -193,6 +194,7 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip, | |||
193 | return 0; | 194 | return 0; |
194 | 195 | ||
195 | error: | 196 | error: |
197 | list_del(&fp->list); /* unlink for avoiding double-free */ | ||
196 | kfree(fp); | 198 | kfree(fp); |
197 | kfree(rate_table); | 199 | kfree(rate_table); |
198 | return err; | 200 | return err; |
@@ -469,6 +471,7 @@ static int create_uaxx_quirk(struct snd_usb_audio *chip, | |||
469 | fp->ep_attr = get_endpoint(alts, 0)->bmAttributes; | 471 | fp->ep_attr = get_endpoint(alts, 0)->bmAttributes; |
470 | fp->datainterval = 0; | 472 | fp->datainterval = 0; |
471 | fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize); | 473 | fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize); |
474 | INIT_LIST_HEAD(&fp->list); | ||
472 | 475 | ||
473 | switch (fp->maxpacksize) { | 476 | switch (fp->maxpacksize) { |
474 | case 0x120: | 477 | case 0x120: |
@@ -492,6 +495,7 @@ static int create_uaxx_quirk(struct snd_usb_audio *chip, | |||
492 | ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK; | 495 | ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK; |
493 | err = snd_usb_add_audio_stream(chip, stream, fp); | 496 | err = snd_usb_add_audio_stream(chip, stream, fp); |
494 | if (err < 0) { | 497 | if (err < 0) { |
498 | list_del(&fp->list); /* unlink for avoiding double-free */ | ||
495 | kfree(fp); | 499 | kfree(fp); |
496 | return err; | 500 | return err; |
497 | } | 501 | } |
diff --git a/sound/usb/stream.c b/sound/usb/stream.c index c4dc577ab1bd..8e9548bc1f1a 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c | |||
@@ -314,7 +314,9 @@ static struct snd_pcm_chmap_elem *convert_chmap(int channels, unsigned int bits, | |||
314 | /* | 314 | /* |
315 | * add this endpoint to the chip instance. | 315 | * add this endpoint to the chip instance. |
316 | * if a stream with the same endpoint already exists, append to it. | 316 | * if a stream with the same endpoint already exists, append to it. |
317 | * if not, create a new pcm stream. | 317 | * if not, create a new pcm stream. note, fp is added to the substream |
318 | * fmt_list and will be freed on the chip instance release. do not free | ||
319 | * fp or do remove it from the substream fmt_list to avoid double-free. | ||
318 | */ | 320 | */ |
319 | int snd_usb_add_audio_stream(struct snd_usb_audio *chip, | 321 | int snd_usb_add_audio_stream(struct snd_usb_audio *chip, |
320 | int stream, | 322 | int stream, |
@@ -675,6 +677,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) | |||
675 | * (fp->maxpacksize & 0x7ff); | 677 | * (fp->maxpacksize & 0x7ff); |
676 | fp->attributes = parse_uac_endpoint_attributes(chip, alts, protocol, iface_no); | 678 | fp->attributes = parse_uac_endpoint_attributes(chip, alts, protocol, iface_no); |
677 | fp->clock = clock; | 679 | fp->clock = clock; |
680 | INIT_LIST_HEAD(&fp->list); | ||
678 | 681 | ||
679 | /* some quirks for attributes here */ | 682 | /* some quirks for attributes here */ |
680 | 683 | ||
@@ -723,6 +726,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) | |||
723 | dev_dbg(&dev->dev, "%u:%d: add audio endpoint %#x\n", iface_no, altno, fp->endpoint); | 726 | dev_dbg(&dev->dev, "%u:%d: add audio endpoint %#x\n", iface_no, altno, fp->endpoint); |
724 | err = snd_usb_add_audio_stream(chip, stream, fp); | 727 | err = snd_usb_add_audio_stream(chip, stream, fp); |
725 | if (err < 0) { | 728 | if (err < 0) { |
729 | list_del(&fp->list); /* unlink for avoiding double-free */ | ||
726 | kfree(fp->rate_table); | 730 | kfree(fp->rate_table); |
727 | kfree(fp->chmap); | 731 | kfree(fp->chmap); |
728 | kfree(fp); | 732 | kfree(fp); |