| From: Takashi Iwai <tiwai@suse.de> |
| Date: Mon, 30 Apr 2018 10:06:48 +0200 |
| Subject: ALSA: aloop: Add missing cable lock to ctl API callbacks |
| |
| commit 76b3421b39bd610546931fc923edcf90c18fa395 upstream. |
| |
| Some control API callbacks in aloop driver are too lazy to take the |
| loopback->cable_lock and it results in possible races of cable access |
| while it's being freed. It eventually lead to a UAF, as reported by |
| fuzzer recently. |
| |
| This patch covers such control API callbacks and add the proper mutex |
| locks. |
| |
| Reported-by: DaeRyong Jeong <threeearcat@gmail.com> |
| Signed-off-by: Takashi Iwai <tiwai@suse.de> |
| [bwh: Backported to 3.16: adjust context] |
| Signed-off-by: Ben Hutchings <ben@decadent.org.uk> |
| --- |
| sound/drivers/aloop.c | 17 +++++++++++++++-- |
| 1 file changed, 15 insertions(+), 2 deletions(-) |
| |
| --- a/sound/drivers/aloop.c |
| +++ b/sound/drivers/aloop.c |
| @@ -829,9 +829,11 @@ static int loopback_rate_shift_get(struc |
| { |
| struct loopback *loopback = snd_kcontrol_chip(kcontrol); |
| |
| + mutex_lock(&loopback->cable_lock); |
| ucontrol->value.integer.value[0] = |
| loopback->setup[kcontrol->id.subdevice] |
| [kcontrol->id.device].rate_shift; |
| + mutex_unlock(&loopback->cable_lock); |
| return 0; |
| } |
| |
| @@ -863,9 +865,11 @@ static int loopback_notify_get(struct sn |
| { |
| struct loopback *loopback = snd_kcontrol_chip(kcontrol); |
| |
| + mutex_lock(&loopback->cable_lock); |
| ucontrol->value.integer.value[0] = |
| loopback->setup[kcontrol->id.subdevice] |
| [kcontrol->id.device].notify; |
| + mutex_unlock(&loopback->cable_lock); |
| return 0; |
| } |
| |
| @@ -877,12 +881,14 @@ static int loopback_notify_put(struct sn |
| int change = 0; |
| |
| val = ucontrol->value.integer.value[0] ? 1 : 0; |
| + mutex_lock(&loopback->cable_lock); |
| if (val != loopback->setup[kcontrol->id.subdevice] |
| [kcontrol->id.device].notify) { |
| loopback->setup[kcontrol->id.subdevice] |
| [kcontrol->id.device].notify = val; |
| change = 1; |
| } |
| + mutex_unlock(&loopback->cable_lock); |
| return change; |
| } |
| |
| @@ -890,13 +896,16 @@ static int loopback_active_get(struct sn |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| struct loopback *loopback = snd_kcontrol_chip(kcontrol); |
| - struct loopback_cable *cable = loopback->cables |
| - [kcontrol->id.subdevice][kcontrol->id.device ^ 1]; |
| + struct loopback_cable *cable; |
| + |
| unsigned int val = 0; |
| |
| + mutex_lock(&loopback->cable_lock); |
| + cable = loopback->cables[kcontrol->id.subdevice][kcontrol->id.device ^ 1]; |
| if (cable != NULL) |
| val = (cable->running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) ? |
| 1 : 0; |
| + mutex_unlock(&loopback->cable_lock); |
| ucontrol->value.integer.value[0] = val; |
| return 0; |
| } |
| @@ -939,9 +948,11 @@ static int loopback_rate_get(struct snd_ |
| { |
| struct loopback *loopback = snd_kcontrol_chip(kcontrol); |
| |
| + mutex_lock(&loopback->cable_lock); |
| ucontrol->value.integer.value[0] = |
| loopback->setup[kcontrol->id.subdevice] |
| [kcontrol->id.device].rate; |
| + mutex_unlock(&loopback->cable_lock); |
| return 0; |
| } |
| |
| @@ -961,9 +972,11 @@ static int loopback_channels_get(struct |
| { |
| struct loopback *loopback = snd_kcontrol_chip(kcontrol); |
| |
| + mutex_lock(&loopback->cable_lock); |
| ucontrol->value.integer.value[0] = |
| loopback->setup[kcontrol->id.subdevice] |
| [kcontrol->id.device].channels; |
| + mutex_unlock(&loopback->cable_lock); |
| return 0; |
| } |
| |