diff options
author | Takashi Iwai <tiwai@suse.de> | 2018-01-05 10:09:47 -0500 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2018-01-05 10:22:20 -0500 |
commit | 9685347aa0a5c2869058ca6ab79fd8e93084a67f (patch) | |
tree | 652ff2b454109ec274639e6ec05825c8601604b7 | |
parent | fb51f1cd06f9ced7b7085a2a4636375d520431ca (diff) |
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: 597603d615d2 ("ALSA: introduce the snd-aloop module for the PCM loopback")
Cc: <stable@vger.kernel.org>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
-rw-r--r-- | sound/drivers/aloop.c | 38 |
1 files changed, 25 insertions, 13 deletions
diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c index afac886ffa28..8b6a39cb7f06 100644 --- a/sound/drivers/aloop.c +++ b/sound/drivers/aloop.c | |||
@@ -658,12 +658,31 @@ static int rule_channels(struct snd_pcm_hw_params *params, | |||
658 | return snd_interval_refine(hw_param_interval(params, rule->var), &t); | 658 | return snd_interval_refine(hw_param_interval(params, rule->var), &t); |
659 | } | 659 | } |
660 | 660 | ||
661 | static void free_cable(struct snd_pcm_substream *substream) | ||
662 | { | ||
663 | struct loopback *loopback = substream->private_data; | ||
664 | int dev = get_cable_index(substream); | ||
665 | struct loopback_cable *cable; | ||
666 | |||
667 | cable = loopback->cables[substream->number][dev]; | ||
668 | if (!cable) | ||
669 | return; | ||
670 | if (cable->streams[!substream->stream]) { | ||
671 | /* other stream is still alive */ | ||
672 | cable->streams[substream->stream] = NULL; | ||
673 | } else { | ||
674 | /* free the cable */ | ||
675 | loopback->cables[substream->number][dev] = NULL; | ||
676 | kfree(cable); | ||
677 | } | ||
678 | } | ||
679 | |||
661 | static int loopback_open(struct snd_pcm_substream *substream) | 680 | static int loopback_open(struct snd_pcm_substream *substream) |
662 | { | 681 | { |
663 | struct snd_pcm_runtime *runtime = substream->runtime; | 682 | struct snd_pcm_runtime *runtime = substream->runtime; |
664 | struct loopback *loopback = substream->private_data; | 683 | struct loopback *loopback = substream->private_data; |
665 | struct loopback_pcm *dpcm; | 684 | struct loopback_pcm *dpcm; |
666 | struct loopback_cable *cable; | 685 | struct loopback_cable *cable = NULL; |
667 | int err = 0; | 686 | int err = 0; |
668 | int dev = get_cable_index(substream); | 687 | int dev = get_cable_index(substream); |
669 | 688 | ||
@@ -681,7 +700,6 @@ static int loopback_open(struct snd_pcm_substream *substream) | |||
681 | if (!cable) { | 700 | if (!cable) { |
682 | cable = kzalloc(sizeof(*cable), GFP_KERNEL); | 701 | cable = kzalloc(sizeof(*cable), GFP_KERNEL); |
683 | if (!cable) { | 702 | if (!cable) { |
684 | kfree(dpcm); | ||
685 | err = -ENOMEM; | 703 | err = -ENOMEM; |
686 | goto unlock; | 704 | goto unlock; |
687 | } | 705 | } |
@@ -723,6 +741,10 @@ static int loopback_open(struct snd_pcm_substream *substream) | |||
723 | else | 741 | else |
724 | runtime->hw = cable->hw; | 742 | runtime->hw = cable->hw; |
725 | unlock: | 743 | unlock: |
744 | if (err < 0) { | ||
745 | free_cable(substream); | ||
746 | kfree(dpcm); | ||
747 | } | ||
726 | mutex_unlock(&loopback->cable_lock); | 748 | mutex_unlock(&loopback->cable_lock); |
727 | return err; | 749 | return err; |
728 | } | 750 | } |
@@ -731,20 +753,10 @@ static int loopback_close(struct snd_pcm_substream *substream) | |||
731 | { | 753 | { |
732 | struct loopback *loopback = substream->private_data; | 754 | struct loopback *loopback = substream->private_data; |
733 | struct loopback_pcm *dpcm = substream->runtime->private_data; | 755 | struct loopback_pcm *dpcm = substream->runtime->private_data; |
734 | struct loopback_cable *cable; | ||
735 | int dev = get_cable_index(substream); | ||
736 | 756 | ||
737 | loopback_timer_stop(dpcm); | 757 | loopback_timer_stop(dpcm); |
738 | mutex_lock(&loopback->cable_lock); | 758 | mutex_lock(&loopback->cable_lock); |
739 | cable = loopback->cables[substream->number][dev]; | 759 | free_cable(substream); |
740 | if (cable->streams[!substream->stream]) { | ||
741 | /* other stream is still alive */ | ||
742 | cable->streams[substream->stream] = NULL; | ||
743 | } else { | ||
744 | /* free the cable */ | ||
745 | loopback->cables[substream->number][dev] = NULL; | ||
746 | kfree(cable); | ||
747 | } | ||
748 | mutex_unlock(&loopback->cable_lock); | 760 | mutex_unlock(&loopback->cable_lock); |
749 | return 0; | 761 | return 0; |
750 | } | 762 | } |