Commit 9685347a authored by Takashi Iwai's avatar Takashi Iwai

ALSA: aloop: Release cable upon open error path

The aloop runtime object and its assignment in the cable are left even
when opening a substream fails.  This doesn't mean any memory leak,
but it still keeps the invalid pointer that may be referred by the
another side of the cable spontaneously, which is a potential Oops
cause.

Clean up the cable assignment and the empty cable upon the error path
properly.

Fixes: 597603d6 ("ALSA: introduce the snd-aloop module for the PCM loopback")
Cc: <stable@vger.kernel.org>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent fb51f1cd
...@@ -658,12 +658,31 @@ static int rule_channels(struct snd_pcm_hw_params *params, ...@@ -658,12 +658,31 @@ static int rule_channels(struct snd_pcm_hw_params *params,
return snd_interval_refine(hw_param_interval(params, rule->var), &t); return snd_interval_refine(hw_param_interval(params, rule->var), &t);
} }
static void free_cable(struct snd_pcm_substream *substream)
{
struct loopback *loopback = substream->private_data;
int dev = get_cable_index(substream);
struct loopback_cable *cable;
cable = loopback->cables[substream->number][dev];
if (!cable)
return;
if (cable->streams[!substream->stream]) {
/* other stream is still alive */
cable->streams[substream->stream] = NULL;
} else {
/* free the cable */
loopback->cables[substream->number][dev] = NULL;
kfree(cable);
}
}
static int loopback_open(struct snd_pcm_substream *substream) static int loopback_open(struct snd_pcm_substream *substream)
{ {
struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_runtime *runtime = substream->runtime;
struct loopback *loopback = substream->private_data; struct loopback *loopback = substream->private_data;
struct loopback_pcm *dpcm; struct loopback_pcm *dpcm;
struct loopback_cable *cable; struct loopback_cable *cable = NULL;
int err = 0; int err = 0;
int dev = get_cable_index(substream); int dev = get_cable_index(substream);
...@@ -681,7 +700,6 @@ static int loopback_open(struct snd_pcm_substream *substream) ...@@ -681,7 +700,6 @@ static int loopback_open(struct snd_pcm_substream *substream)
if (!cable) { if (!cable) {
cable = kzalloc(sizeof(*cable), GFP_KERNEL); cable = kzalloc(sizeof(*cable), GFP_KERNEL);
if (!cable) { if (!cable) {
kfree(dpcm);
err = -ENOMEM; err = -ENOMEM;
goto unlock; goto unlock;
} }
...@@ -723,6 +741,10 @@ static int loopback_open(struct snd_pcm_substream *substream) ...@@ -723,6 +741,10 @@ static int loopback_open(struct snd_pcm_substream *substream)
else else
runtime->hw = cable->hw; runtime->hw = cable->hw;
unlock: unlock:
if (err < 0) {
free_cable(substream);
kfree(dpcm);
}
mutex_unlock(&loopback->cable_lock); mutex_unlock(&loopback->cable_lock);
return err; return err;
} }
...@@ -731,20 +753,10 @@ static int loopback_close(struct snd_pcm_substream *substream) ...@@ -731,20 +753,10 @@ static int loopback_close(struct snd_pcm_substream *substream)
{ {
struct loopback *loopback = substream->private_data; struct loopback *loopback = substream->private_data;
struct loopback_pcm *dpcm = substream->runtime->private_data; struct loopback_pcm *dpcm = substream->runtime->private_data;
struct loopback_cable *cable;
int dev = get_cable_index(substream);
loopback_timer_stop(dpcm); loopback_timer_stop(dpcm);
mutex_lock(&loopback->cable_lock); mutex_lock(&loopback->cable_lock);
cable = loopback->cables[substream->number][dev]; free_cable(substream);
if (cable->streams[!substream->stream]) {
/* other stream is still alive */
cable->streams[substream->stream] = NULL;
} else {
/* free the cable */
loopback->cables[substream->number][dev] = NULL;
kfree(cable);
}
mutex_unlock(&loopback->cable_lock); mutex_unlock(&loopback->cable_lock);
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment