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: 7edf3b5e6a ("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>
master
parent
b32ae47a2b
commit
c39f0bc03f
|
|
@ -125,7 +125,7 @@ static int add_audio_stream_from_fixed_fmt(struct snd_usb_audio *chip,
|
|||
|
||||
snd_usb_audioformat_set_sync_ep(chip, fp);
|
||||
|
||||
err = snd_usb_add_audio_stream(chip, stream, fp);
|
||||
err = snd_usb_add_audio_stream(chip, stream, fp, NULL);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ static void snd_usb_audio_pcm_free(struct snd_pcm *pcm)
|
|||
static void snd_usb_init_substream(struct snd_usb_stream *as,
|
||||
int stream,
|
||||
struct audioformat *fp,
|
||||
struct snd_usb_power_domain *pd)
|
||||
struct snd_usb_power_domain **pdptr)
|
||||
{
|
||||
struct snd_usb_substream *subs = &as->substream[stream];
|
||||
|
||||
|
|
@ -105,10 +105,11 @@ static void snd_usb_init_substream(struct snd_usb_stream *as,
|
|||
if (fp->channels > subs->channels_max)
|
||||
subs->channels_max = fp->channels;
|
||||
|
||||
if (pd) {
|
||||
subs->str_pd = pd;
|
||||
if (pdptr && *pdptr) {
|
||||
subs->str_pd = *pdptr;
|
||||
*pdptr = NULL; /* assigned */
|
||||
/* Initialize Power Domain to idle status D1 */
|
||||
snd_usb_power_domain_set(subs->stream->chip, pd,
|
||||
snd_usb_power_domain_set(subs->stream->chip, subs->str_pd,
|
||||
UAC3_PD_STATE_D1);
|
||||
}
|
||||
|
||||
|
|
@ -492,11 +493,14 @@ snd_pcm_chmap_elem *convert_chmap_v3(struct uac3_cluster_header_descriptor
|
|||
* if not, create a new pcm stream. note, fp is added to the substream
|
||||
* fmt_list and will be freed on the chip instance release. do not free
|
||||
* fp or do remove it from the substream fmt_list to avoid double-free.
|
||||
*
|
||||
* pdptr is optional and can be NULL. When it's non-NULL and the PD gets
|
||||
* assigned to the stream, *pdptr is cleared to NULL upon return.
|
||||
*/
|
||||
static int __snd_usb_add_audio_stream(struct snd_usb_audio *chip,
|
||||
int stream,
|
||||
struct audioformat *fp,
|
||||
struct snd_usb_power_domain *pd)
|
||||
int snd_usb_add_audio_stream(struct snd_usb_audio *chip,
|
||||
int stream,
|
||||
struct audioformat *fp,
|
||||
struct snd_usb_power_domain **pdptr)
|
||||
|
||||
{
|
||||
struct snd_usb_stream *as;
|
||||
|
|
@ -529,7 +533,7 @@ static int __snd_usb_add_audio_stream(struct snd_usb_audio *chip,
|
|||
err = snd_pcm_new_stream(as->pcm, stream, 1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
snd_usb_init_substream(as, stream, fp, pd);
|
||||
snd_usb_init_substream(as, stream, fp, pdptr);
|
||||
return add_chmap(as->pcm, stream, subs);
|
||||
}
|
||||
|
||||
|
|
@ -558,7 +562,7 @@ static int __snd_usb_add_audio_stream(struct snd_usb_audio *chip,
|
|||
else
|
||||
strscpy(pcm->name, "USB Audio");
|
||||
|
||||
snd_usb_init_substream(as, stream, fp, pd);
|
||||
snd_usb_init_substream(as, stream, fp, pdptr);
|
||||
|
||||
/*
|
||||
* Keep using head insertion for M-Audio Audiophile USB (tm) which has a
|
||||
|
|
@ -576,21 +580,6 @@ static int __snd_usb_add_audio_stream(struct snd_usb_audio *chip,
|
|||
return add_chmap(pcm, stream, &as->substream[stream]);
|
||||
}
|
||||
|
||||
int snd_usb_add_audio_stream(struct snd_usb_audio *chip,
|
||||
int stream,
|
||||
struct audioformat *fp)
|
||||
{
|
||||
return __snd_usb_add_audio_stream(chip, stream, fp, NULL);
|
||||
}
|
||||
|
||||
static int snd_usb_add_audio_stream_v3(struct snd_usb_audio *chip,
|
||||
int stream,
|
||||
struct audioformat *fp,
|
||||
struct snd_usb_power_domain *pd)
|
||||
{
|
||||
return __snd_usb_add_audio_stream(chip, stream, fp, pd);
|
||||
}
|
||||
|
||||
static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip,
|
||||
struct usb_host_interface *alts,
|
||||
int protocol, int iface_no)
|
||||
|
|
@ -1113,8 +1102,7 @@ found_clock:
|
|||
}
|
||||
}
|
||||
|
||||
if (pd)
|
||||
*pd_out = pd;
|
||||
*pd_out = pd;
|
||||
|
||||
return fp;
|
||||
}
|
||||
|
|
@ -1129,7 +1117,6 @@ static int __snd_usb_parse_audio_interface(struct snd_usb_audio *chip,
|
|||
struct usb_interface_descriptor *altsd;
|
||||
int i, altno, err, stream;
|
||||
struct audioformat *fp = NULL;
|
||||
struct snd_usb_power_domain *pd = NULL;
|
||||
bool set_iface_first;
|
||||
int num, protocol;
|
||||
|
||||
|
|
@ -1171,6 +1158,12 @@ static int __snd_usb_parse_audio_interface(struct snd_usb_audio *chip,
|
|||
if (snd_usb_apply_interface_quirk(chip, iface_no, altno))
|
||||
continue;
|
||||
|
||||
/* pd may be allocated at snd_usb_get_audioformat_uac3() and
|
||||
* assigned at snd_usb_add_audio_stream(); otherwise it'll be
|
||||
* freed automatically by cleanup at each loop.
|
||||
*/
|
||||
struct snd_usb_power_domain *pd __free(kfree) = NULL;
|
||||
|
||||
/*
|
||||
* Roland audio streaming interfaces are marked with protocols
|
||||
* 0/1/2, but are UAC 1 compatible.
|
||||
|
|
@ -1226,23 +1219,16 @@ static int __snd_usb_parse_audio_interface(struct snd_usb_audio *chip,
|
|||
*has_non_pcm = true;
|
||||
if ((fp->fmt_type == UAC_FORMAT_TYPE_I) == non_pcm) {
|
||||
audioformat_free(fp);
|
||||
kfree(pd);
|
||||
fp = NULL;
|
||||
pd = NULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
snd_usb_audioformat_set_sync_ep(chip, fp);
|
||||
|
||||
dev_dbg(&dev->dev, "%u:%d: add audio endpoint %#x\n", iface_no, altno, fp->endpoint);
|
||||
if (protocol == UAC_VERSION_3)
|
||||
err = snd_usb_add_audio_stream_v3(chip, stream, fp, pd);
|
||||
else
|
||||
err = snd_usb_add_audio_stream(chip, stream, fp);
|
||||
|
||||
err = snd_usb_add_audio_stream(chip, stream, fp, &pd);
|
||||
if (err < 0) {
|
||||
audioformat_free(fp);
|
||||
kfree(pd);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip,
|
|||
|
||||
int snd_usb_add_audio_stream(struct snd_usb_audio *chip,
|
||||
int stream,
|
||||
struct audioformat *fp);
|
||||
struct audioformat *fp,
|
||||
struct snd_usb_power_domain **pdptr);
|
||||
|
||||
#endif /* __USBAUDIO_STREAM_H */
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue