aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLars-Peter Clausen <lars@metafoo.de>2014-06-18 07:32:32 -0400
committerTakashi Iwai <tiwai@suse.de>2014-06-18 09:12:49 -0400
commit82262a46627bebb0febcc26664746c25cef08563 (patch)
treea6294ee793eed8fa0f6307fc7d02a7485178179f
parent07f4d9d74a04aa7c72c5dae0ef97565f28f17b92 (diff)
ALSA: control: Fix replacing user controls
There are two issues with the current implementation for replacing user controls. The first is that the code does not check if the control is actually a user control and neither does it check if the control is owned by the process that tries to remove it. That allows userspace applications to remove arbitrary controls, which can cause a user after free if a for example a driver does not expect a control to be removed from under its feed. The second issue is that on one hand when a control is replaced the user_ctl_count limit is not checked and on the other hand the user_ctl_count is increased (even though the number of user controls does not change). This allows userspace, once the user_ctl_count limit as been reached, to repeatedly replace a control until user_ctl_count overflows. Once that happens new controls can be added effectively bypassing the user_ctl_count limit. Both issues can be fixed by instead of open-coding the removal of the control that is to be replaced to use snd_ctl_remove_user_ctl(). This function does proper permission checks as well as decrements user_ctl_count after the control has been removed. Note that by using snd_ctl_remove_user_ctl() the check which returns -EBUSY at beginning of the function if the control already exists is removed. This is not a problem though since the check is quite useless, because the lock that is protecting the control list is released between the check and before adding the new control to the list, which means that it is possible that a different control with the same settings is added to the list after the check. Luckily there is another check that is done while holding the lock in snd_ctl_add(), so we'll rely on that to make sure that the same control is not added twice. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> Acked-by: Jaroslav Kysela <perex@perex.cz> Cc: <stable@vger.kernel.org> Signed-off-by: Takashi Iwai <tiwai@suse.de>
-rw-r--r--sound/core/control.c25
1 files changed, 9 insertions, 16 deletions
diff --git a/sound/core/control.c b/sound/core/control.c
index 00ab034f5fcb..1f413c286511 100644
--- a/sound/core/control.c
+++ b/sound/core/control.c
@@ -1154,8 +1154,6 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
1154 struct user_element *ue; 1154 struct user_element *ue;
1155 int idx, err; 1155 int idx, err;
1156 1156
1157 if (!replace && card->user_ctl_count >= MAX_USER_CONTROLS)
1158 return -ENOMEM;
1159 if (info->count < 1) 1157 if (info->count < 1)
1160 return -EINVAL; 1158 return -EINVAL;
1161 access = info->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE : 1159 access = info->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE :
@@ -1164,21 +1162,16 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
1164 SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE)); 1162 SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE));
1165 info->id.numid = 0; 1163 info->id.numid = 0;
1166 memset(&kctl, 0, sizeof(kctl)); 1164 memset(&kctl, 0, sizeof(kctl));
1167 down_write(&card->controls_rwsem); 1165
1168 _kctl = snd_ctl_find_id(card, &info->id); 1166 if (replace) {
1169 err = 0; 1167 err = snd_ctl_remove_user_ctl(file, &info->id);
1170 if (_kctl) { 1168 if (err)
1171 if (replace) 1169 return err;
1172 err = snd_ctl_remove(card, _kctl);
1173 else
1174 err = -EBUSY;
1175 } else {
1176 if (replace)
1177 err = -ENOENT;
1178 } 1170 }
1179 up_write(&card->controls_rwsem); 1171
1180 if (err < 0) 1172 if (card->user_ctl_count >= MAX_USER_CONTROLS)
1181 return err; 1173 return -ENOMEM;
1174
1182 memcpy(&kctl.id, &info->id, sizeof(info->id)); 1175 memcpy(&kctl.id, &info->id, sizeof(info->id));
1183 kctl.count = info->owner ? info->owner : 1; 1176 kctl.count = info->owner ? info->owner : 1;
1184 access |= SNDRV_CTL_ELEM_ACCESS_USER; 1177 access |= SNDRV_CTL_ELEM_ACCESS_USER;