Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+25 −13
Original line number Original line Diff line number Diff line
@@ -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)
	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)
	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)
{
{
	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;
}
}