Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

ALSA: usb-audio: Fix potential leak of pd at parsing UAC3 streams

At parsing UAC3 streams, we allocate a PD object at each time, and
either assign or free it. But there is a case where the PD object may
be leaked; namely, in __snd_usb_parse_audio_interface() loop, when an
audioformat shares the same endpoint with others, it's put to a link
and returns from snd_usb_add_audio_stream(), but the PD is forgotten
afterwards. Overall, the treatment of PD object in the parser code is
a bit flaky, and we should be more careful about the object ownership.

This patch tries to fix the above case and improve the code a bit.
The pd object is now managed with the auto-cleanup in the loop, and
the ownership is updated when the pd object gets assigned to the
stream, which guarantees the release of the leftover object.

Fixes: 7edf3b5e6a45 ("ALSA: usb-audio: AudioStreaming Power Domain parsing")
Link: https://patch.msgid.link/20260427151508.12544-1-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>

+25 -38
+1 -1
sound/usb/quirks.c
··· 125 125 126 126 snd_usb_audioformat_set_sync_ep(chip, fp); 127 127 128 - err = snd_usb_add_audio_stream(chip, stream, fp); 128 + err = snd_usb_add_audio_stream(chip, stream, fp, NULL); 129 129 if (err < 0) 130 130 return err; 131 131
+22 -36
sound/usb/stream.c
··· 79 79 static void snd_usb_init_substream(struct snd_usb_stream *as, 80 80 int stream, 81 81 struct audioformat *fp, 82 - struct snd_usb_power_domain *pd) 82 + struct snd_usb_power_domain **pdptr) 83 83 { 84 84 struct snd_usb_substream *subs = &as->substream[stream]; 85 85 ··· 105 105 if (fp->channels > subs->channels_max) 106 106 subs->channels_max = fp->channels; 107 107 108 - if (pd) { 109 - subs->str_pd = pd; 108 + if (pdptr && *pdptr) { 109 + subs->str_pd = *pdptr; 110 + *pdptr = NULL; /* assigned */ 110 111 /* Initialize Power Domain to idle status D1 */ 111 - snd_usb_power_domain_set(subs->stream->chip, pd, 112 + snd_usb_power_domain_set(subs->stream->chip, subs->str_pd, 112 113 UAC3_PD_STATE_D1); 113 114 } 114 115 ··· 493 492 * if not, create a new pcm stream. note, fp is added to the substream 494 493 * fmt_list and will be freed on the chip instance release. do not free 495 494 * fp or do remove it from the substream fmt_list to avoid double-free. 495 + * 496 + * pdptr is optional and can be NULL. When it's non-NULL and the PD gets 497 + * assigned to the stream, *pdptr is cleared to NULL upon return. 496 498 */ 497 - static int __snd_usb_add_audio_stream(struct snd_usb_audio *chip, 498 - int stream, 499 - struct audioformat *fp, 500 - struct snd_usb_power_domain *pd) 499 + int snd_usb_add_audio_stream(struct snd_usb_audio *chip, 500 + int stream, 501 + struct audioformat *fp, 502 + struct snd_usb_power_domain **pdptr) 501 503 502 504 { 503 505 struct snd_usb_stream *as; ··· 533 529 err = snd_pcm_new_stream(as->pcm, stream, 1); 534 530 if (err < 0) 535 531 return err; 536 - snd_usb_init_substream(as, stream, fp, pd); 532 + snd_usb_init_substream(as, stream, fp, pdptr); 537 533 return add_chmap(as->pcm, stream, subs); 538 534 } 539 535 ··· 562 558 else 563 559 strscpy(pcm->name, "USB Audio"); 564 560 565 - snd_usb_init_substream(as, stream, fp, pd); 561 + snd_usb_init_substream(as, stream, fp, pdptr); 566 562 567 563 /* 568 564 * Keep using head insertion for M-Audio Audiophile USB (tm) which has a ··· 578 574 snd_usb_proc_pcm_format_add(as); 579 575 580 576 return add_chmap(pcm, stream, &as->substream[stream]); 581 - } 582 - 583 - int snd_usb_add_audio_stream(struct snd_usb_audio *chip, 584 - int stream, 585 - struct audioformat *fp) 586 - { 587 - return __snd_usb_add_audio_stream(chip, stream, fp, NULL); 588 - } 589 - 590 - static int snd_usb_add_audio_stream_v3(struct snd_usb_audio *chip, 591 - int stream, 592 - struct audioformat *fp, 593 - struct snd_usb_power_domain *pd) 594 - { 595 - return __snd_usb_add_audio_stream(chip, stream, fp, pd); 596 577 } 597 578 598 579 static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip, ··· 1102 1113 } 1103 1114 } 1104 1115 1105 - if (pd) 1106 - *pd_out = pd; 1116 + *pd_out = pd; 1107 1117 1108 1118 return fp; 1109 1119 } ··· 1117 1129 struct usb_interface_descriptor *altsd; 1118 1130 int i, altno, err, stream; 1119 1131 struct audioformat *fp = NULL; 1120 - struct snd_usb_power_domain *pd = NULL; 1121 1132 bool set_iface_first; 1122 1133 int num, protocol; 1123 1134 ··· 1157 1170 1158 1171 if (snd_usb_apply_interface_quirk(chip, iface_no, altno)) 1159 1172 continue; 1173 + 1174 + /* pd may be allocated at snd_usb_get_audioformat_uac3() and 1175 + * assigned at snd_usb_add_audio_stream(); otherwise it'll be 1176 + * freed automatically by cleanup at each loop. 1177 + */ 1178 + struct snd_usb_power_domain *pd __free(kfree) = NULL; 1160 1179 1161 1180 /* 1162 1181 * Roland audio streaming interfaces are marked with protocols ··· 1219 1226 *has_non_pcm = true; 1220 1227 if ((fp->fmt_type == UAC_FORMAT_TYPE_I) == non_pcm) { 1221 1228 audioformat_free(fp); 1222 - kfree(pd); 1223 1229 fp = NULL; 1224 - pd = NULL; 1225 1230 continue; 1226 1231 } 1227 1232 1228 1233 snd_usb_audioformat_set_sync_ep(chip, fp); 1229 1234 1230 1235 dev_dbg(&dev->dev, "%u:%d: add audio endpoint %#x\n", iface_no, altno, fp->endpoint); 1231 - if (protocol == UAC_VERSION_3) 1232 - err = snd_usb_add_audio_stream_v3(chip, stream, fp, pd); 1233 - else 1234 - err = snd_usb_add_audio_stream(chip, stream, fp); 1235 - 1236 + err = snd_usb_add_audio_stream(chip, stream, fp, &pd); 1236 1237 if (err < 0) { 1237 1238 audioformat_free(fp); 1238 - kfree(pd); 1239 1239 return err; 1240 1240 } 1241 1241
+2 -1
sound/usb/stream.h
··· 7 7 8 8 int snd_usb_add_audio_stream(struct snd_usb_audio *chip, 9 9 int stream, 10 - struct audioformat *fp); 10 + struct audioformat *fp, 11 + struct snd_usb_power_domain **pdptr); 11 12 12 13 #endif /* __USBAUDIO_STREAM_H */ 13 14