ALSA: aloop: Add missing cable lock to ctl API callbacks
authorTakashi Iwai <tiwai@suse.de>
Mon, 30 Apr 2018 08:06:48 +0000 (10:06 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 9 May 2018 07:51:50 +0000 (09:51 +0200)
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>
Cc: <stable@vger.kernel.org>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
sound/drivers/aloop.c

index 800fdf1..dfd30a8 100644 (file)
@@ -832,9 +832,11 @@ static int loopback_rate_shift_get(struct snd_kcontrol *kcontrol,
 {
        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;
 }
 
@@ -866,9 +868,11 @@ static int loopback_notify_get(struct snd_kcontrol *kcontrol,
 {
        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;
 }
 
@@ -880,12 +884,14 @@ static int loopback_notify_put(struct snd_kcontrol *kcontrol,
        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;
 }
 
@@ -893,15 +899,18 @@ static int loopback_active_get(struct snd_kcontrol *kcontrol,
                               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) {
                unsigned int running = cable->running ^ cable->pause;
 
                val = (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) ? 1 : 0;
        }
+       mutex_unlock(&loopback->cable_lock);
        ucontrol->value.integer.value[0] = val;
        return 0;
 }
@@ -944,9 +953,11 @@ static int loopback_rate_get(struct snd_kcontrol *kcontrol,
 {
        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;
 }
 
@@ -966,9 +977,11 @@ static int loopback_channels_get(struct snd_kcontrol *kcontrol,
 {
        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;
 }