aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorLars-Peter Clausen <lars@metafoo.de>2014-06-18 07:32:31 -0400
committerTakashi Iwai <tiwai@suse.de>2014-06-18 09:12:33 -0400
commit07f4d9d74a04aa7c72c5dae0ef97565f28f17b92 (patch)
tree65d9ee5f96dacb86a33d3a8cd786e876563d93ac /sound
parent7171511eaec5bf23fb06078f59784a3a0626b38f (diff)
ALSA: control: Protect user controls against concurrent access
The user-control put and get handlers as well as the tlv do not protect against concurrent access from multiple threads. Since the state of the control is not updated atomically it is possible that either two write operations or a write and a read operation race against each other. Both can lead to arbitrary memory disclosure. This patch introduces a new lock that protects user-controls from concurrent access. Since applications typically access controls sequentially than in parallel a single lock per card should be fine. 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>
Diffstat (limited to 'sound')
-rw-r--r--sound/core/control.c31
-rw-r--r--sound/core/init.c1
2 files changed, 26 insertions, 6 deletions
diff --git a/sound/core/control.c b/sound/core/control.c
index f038f5afafe2..00ab034f5fcb 100644
--- a/sound/core/control.c
+++ b/sound/core/control.c
@@ -991,6 +991,7 @@ static int snd_ctl_elem_unlock(struct snd_ctl_file *file,
991 991
992struct user_element { 992struct user_element {
993 struct snd_ctl_elem_info info; 993 struct snd_ctl_elem_info info;
994 struct snd_card *card;
994 void *elem_data; /* element data */ 995 void *elem_data; /* element data */
995 unsigned long elem_data_size; /* size of element data in bytes */ 996 unsigned long elem_data_size; /* size of element data in bytes */
996 void *tlv_data; /* TLV data */ 997 void *tlv_data; /* TLV data */
@@ -1034,7 +1035,9 @@ static int snd_ctl_elem_user_get(struct snd_kcontrol *kcontrol,
1034{ 1035{
1035 struct user_element *ue = kcontrol->private_data; 1036 struct user_element *ue = kcontrol->private_data;
1036 1037
1038 mutex_lock(&ue->card->user_ctl_lock);
1037 memcpy(&ucontrol->value, ue->elem_data, ue->elem_data_size); 1039 memcpy(&ucontrol->value, ue->elem_data, ue->elem_data_size);
1040 mutex_unlock(&ue->card->user_ctl_lock);
1038 return 0; 1041 return 0;
1039} 1042}
1040 1043
@@ -1043,10 +1046,12 @@ static int snd_ctl_elem_user_put(struct snd_kcontrol *kcontrol,
1043{ 1046{
1044 int change; 1047 int change;
1045 struct user_element *ue = kcontrol->private_data; 1048 struct user_element *ue = kcontrol->private_data;
1046 1049
1050 mutex_lock(&ue->card->user_ctl_lock);
1047 change = memcmp(&ucontrol->value, ue->elem_data, ue->elem_data_size) != 0; 1051 change = memcmp(&ucontrol->value, ue->elem_data, ue->elem_data_size) != 0;
1048 if (change) 1052 if (change)
1049 memcpy(ue->elem_data, &ucontrol->value, ue->elem_data_size); 1053 memcpy(ue->elem_data, &ucontrol->value, ue->elem_data_size);
1054 mutex_unlock(&ue->card->user_ctl_lock);
1050 return change; 1055 return change;
1051} 1056}
1052 1057
@@ -1066,19 +1071,32 @@ static int snd_ctl_elem_user_tlv(struct snd_kcontrol *kcontrol,
1066 new_data = memdup_user(tlv, size); 1071 new_data = memdup_user(tlv, size);
1067 if (IS_ERR(new_data)) 1072 if (IS_ERR(new_data))
1068 return PTR_ERR(new_data); 1073 return PTR_ERR(new_data);
1074 mutex_lock(&ue->card->user_ctl_lock);
1069 change = ue->tlv_data_size != size; 1075 change = ue->tlv_data_size != size;
1070 if (!change) 1076 if (!change)
1071 change = memcmp(ue->tlv_data, new_data, size); 1077 change = memcmp(ue->tlv_data, new_data, size);
1072 kfree(ue->tlv_data); 1078 kfree(ue->tlv_data);
1073 ue->tlv_data = new_data; 1079 ue->tlv_data = new_data;
1074 ue->tlv_data_size = size; 1080 ue->tlv_data_size = size;
1081 mutex_unlock(&ue->card->user_ctl_lock);
1075 } else { 1082 } else {
1076 if (! ue->tlv_data_size || ! ue->tlv_data) 1083 int ret = 0;
1077 return -ENXIO; 1084
1078 if (size < ue->tlv_data_size) 1085 mutex_lock(&ue->card->user_ctl_lock);
1079 return -ENOSPC; 1086 if (!ue->tlv_data_size || !ue->tlv_data) {
1087 ret = -ENXIO;
1088 goto err_unlock;
1089 }
1090 if (size < ue->tlv_data_size) {
1091 ret = -ENOSPC;
1092 goto err_unlock;
1093 }
1080 if (copy_to_user(tlv, ue->tlv_data, ue->tlv_data_size)) 1094 if (copy_to_user(tlv, ue->tlv_data, ue->tlv_data_size))
1081 return -EFAULT; 1095 ret = -EFAULT;
1096err_unlock:
1097 mutex_unlock(&ue->card->user_ctl_lock);
1098 if (ret)
1099 return ret;
1082 } 1100 }
1083 return change; 1101 return change;
1084} 1102}
@@ -1210,6 +1228,7 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
1210 ue = kzalloc(sizeof(struct user_element) + private_size, GFP_KERNEL); 1228 ue = kzalloc(sizeof(struct user_element) + private_size, GFP_KERNEL);
1211 if (ue == NULL) 1229 if (ue == NULL)
1212 return -ENOMEM; 1230 return -ENOMEM;
1231 ue->card = card;
1213 ue->info = *info; 1232 ue->info = *info;
1214 ue->info.access = 0; 1233 ue->info.access = 0;
1215 ue->elem_data = (char *)ue + sizeof(*ue); 1234 ue->elem_data = (char *)ue + sizeof(*ue);
diff --git a/sound/core/init.c b/sound/core/init.c
index 5ee83845c5de..7bdfd19e24a8 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -232,6 +232,7 @@ int snd_card_new(struct device *parent, int idx, const char *xid,
232 INIT_LIST_HEAD(&card->devices); 232 INIT_LIST_HEAD(&card->devices);
233 init_rwsem(&card->controls_rwsem); 233 init_rwsem(&card->controls_rwsem);
234 rwlock_init(&card->ctl_files_rwlock); 234 rwlock_init(&card->ctl_files_rwlock);
235 mutex_init(&card->user_ctl_lock);
235 INIT_LIST_HEAD(&card->controls); 236 INIT_LIST_HEAD(&card->controls);
236 INIT_LIST_HEAD(&card->ctl_files); 237 INIT_LIST_HEAD(&card->ctl_files);
237 spin_lock_init(&card->files_lock); 238 spin_lock_init(&card->files_lock);