Commit 8d42fda9 authored by Takashi Iwai's avatar Takashi Iwai

Merge branch 'topic/core-vuln-fixes' into for-linus

parents 6c0c9a3d 883a1d49
...@@ -116,6 +116,8 @@ struct snd_card { ...@@ -116,6 +116,8 @@ struct snd_card {
int user_ctl_count; /* count of all user controls */ int user_ctl_count; /* count of all user controls */
struct list_head controls; /* all controls for this card */ struct list_head controls; /* all controls for this card */
struct list_head ctl_files; /* active control files */ struct list_head ctl_files; /* active control files */
struct mutex user_ctl_lock; /* protects user controls against
concurrent access */
struct snd_info_entry *proc_root; /* root for soundcard specific files */ struct snd_info_entry *proc_root; /* root for soundcard specific files */
struct snd_info_entry *proc_id; /* the card id */ struct snd_info_entry *proc_id; /* the card id */
......
...@@ -288,6 +288,10 @@ static bool snd_ctl_remove_numid_conflict(struct snd_card *card, ...@@ -288,6 +288,10 @@ static bool snd_ctl_remove_numid_conflict(struct snd_card *card,
{ {
struct snd_kcontrol *kctl; struct snd_kcontrol *kctl;
/* Make sure that the ids assigned to the control do not wrap around */
if (card->last_numid >= UINT_MAX - count)
card->last_numid = 0;
list_for_each_entry(kctl, &card->controls, list) { list_for_each_entry(kctl, &card->controls, list) {
if (kctl->id.numid < card->last_numid + 1 + count && if (kctl->id.numid < card->last_numid + 1 + count &&
kctl->id.numid + kctl->count > card->last_numid + 1) { kctl->id.numid + kctl->count > card->last_numid + 1) {
...@@ -330,6 +334,7 @@ int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol) ...@@ -330,6 +334,7 @@ int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol)
{ {
struct snd_ctl_elem_id id; struct snd_ctl_elem_id id;
unsigned int idx; unsigned int idx;
unsigned int count;
int err = -EINVAL; int err = -EINVAL;
if (! kcontrol) if (! kcontrol)
...@@ -337,6 +342,9 @@ int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol) ...@@ -337,6 +342,9 @@ int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol)
if (snd_BUG_ON(!card || !kcontrol->info)) if (snd_BUG_ON(!card || !kcontrol->info))
goto error; goto error;
id = kcontrol->id; id = kcontrol->id;
if (id.index > UINT_MAX - kcontrol->count)
goto error;
down_write(&card->controls_rwsem); down_write(&card->controls_rwsem);
if (snd_ctl_find_id(card, &id)) { if (snd_ctl_find_id(card, &id)) {
up_write(&card->controls_rwsem); up_write(&card->controls_rwsem);
...@@ -358,8 +366,9 @@ int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol) ...@@ -358,8 +366,9 @@ int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol)
card->controls_count += kcontrol->count; card->controls_count += kcontrol->count;
kcontrol->id.numid = card->last_numid + 1; kcontrol->id.numid = card->last_numid + 1;
card->last_numid += kcontrol->count; card->last_numid += kcontrol->count;
count = kcontrol->count;
up_write(&card->controls_rwsem); up_write(&card->controls_rwsem);
for (idx = 0; idx < kcontrol->count; idx++, id.index++, id.numid++) for (idx = 0; idx < count; idx++, id.index++, id.numid++)
snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_ADD, &id); snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_ADD, &id);
return 0; return 0;
...@@ -388,6 +397,7 @@ int snd_ctl_replace(struct snd_card *card, struct snd_kcontrol *kcontrol, ...@@ -388,6 +397,7 @@ int snd_ctl_replace(struct snd_card *card, struct snd_kcontrol *kcontrol,
bool add_on_replace) bool add_on_replace)
{ {
struct snd_ctl_elem_id id; struct snd_ctl_elem_id id;
unsigned int count;
unsigned int idx; unsigned int idx;
struct snd_kcontrol *old; struct snd_kcontrol *old;
int ret; int ret;
...@@ -423,8 +433,9 @@ int snd_ctl_replace(struct snd_card *card, struct snd_kcontrol *kcontrol, ...@@ -423,8 +433,9 @@ int snd_ctl_replace(struct snd_card *card, struct snd_kcontrol *kcontrol,
card->controls_count += kcontrol->count; card->controls_count += kcontrol->count;
kcontrol->id.numid = card->last_numid + 1; kcontrol->id.numid = card->last_numid + 1;
card->last_numid += kcontrol->count; card->last_numid += kcontrol->count;
count = kcontrol->count;
up_write(&card->controls_rwsem); up_write(&card->controls_rwsem);
for (idx = 0; idx < kcontrol->count; idx++, id.index++, id.numid++) for (idx = 0; idx < count; idx++, id.index++, id.numid++)
snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_ADD, &id); snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_ADD, &id);
return 0; return 0;
...@@ -897,9 +908,9 @@ static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file, ...@@ -897,9 +908,9 @@ static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file,
result = kctl->put(kctl, control); result = kctl->put(kctl, control);
} }
if (result > 0) { if (result > 0) {
struct snd_ctl_elem_id id = control->id;
up_read(&card->controls_rwsem); up_read(&card->controls_rwsem);
snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &id);
&control->id);
return 0; return 0;
} }
} }
...@@ -991,6 +1002,7 @@ static int snd_ctl_elem_unlock(struct snd_ctl_file *file, ...@@ -991,6 +1002,7 @@ static int snd_ctl_elem_unlock(struct snd_ctl_file *file,
struct user_element { struct user_element {
struct snd_ctl_elem_info info; struct snd_ctl_elem_info info;
struct snd_card *card;
void *elem_data; /* element data */ void *elem_data; /* element data */
unsigned long elem_data_size; /* size of element data in bytes */ unsigned long elem_data_size; /* size of element data in bytes */
void *tlv_data; /* TLV data */ void *tlv_data; /* TLV data */
...@@ -1034,7 +1046,9 @@ static int snd_ctl_elem_user_get(struct snd_kcontrol *kcontrol, ...@@ -1034,7 +1046,9 @@ static int snd_ctl_elem_user_get(struct snd_kcontrol *kcontrol,
{ {
struct user_element *ue = kcontrol->private_data; struct user_element *ue = kcontrol->private_data;
mutex_lock(&ue->card->user_ctl_lock);
memcpy(&ucontrol->value, ue->elem_data, ue->elem_data_size); memcpy(&ucontrol->value, ue->elem_data, ue->elem_data_size);
mutex_unlock(&ue->card->user_ctl_lock);
return 0; return 0;
} }
...@@ -1043,10 +1057,12 @@ static int snd_ctl_elem_user_put(struct snd_kcontrol *kcontrol, ...@@ -1043,10 +1057,12 @@ static int snd_ctl_elem_user_put(struct snd_kcontrol *kcontrol,
{ {
int change; int change;
struct user_element *ue = kcontrol->private_data; struct user_element *ue = kcontrol->private_data;
mutex_lock(&ue->card->user_ctl_lock);
change = memcmp(&ucontrol->value, ue->elem_data, ue->elem_data_size) != 0; change = memcmp(&ucontrol->value, ue->elem_data, ue->elem_data_size) != 0;
if (change) if (change)
memcpy(ue->elem_data, &ucontrol->value, ue->elem_data_size); memcpy(ue->elem_data, &ucontrol->value, ue->elem_data_size);
mutex_unlock(&ue->card->user_ctl_lock);
return change; return change;
} }
...@@ -1066,19 +1082,32 @@ static int snd_ctl_elem_user_tlv(struct snd_kcontrol *kcontrol, ...@@ -1066,19 +1082,32 @@ static int snd_ctl_elem_user_tlv(struct snd_kcontrol *kcontrol,
new_data = memdup_user(tlv, size); new_data = memdup_user(tlv, size);
if (IS_ERR(new_data)) if (IS_ERR(new_data))
return PTR_ERR(new_data); return PTR_ERR(new_data);
mutex_lock(&ue->card->user_ctl_lock);
change = ue->tlv_data_size != size; change = ue->tlv_data_size != size;
if (!change) if (!change)
change = memcmp(ue->tlv_data, new_data, size); change = memcmp(ue->tlv_data, new_data, size);
kfree(ue->tlv_data); kfree(ue->tlv_data);
ue->tlv_data = new_data; ue->tlv_data = new_data;
ue->tlv_data_size = size; ue->tlv_data_size = size;
mutex_unlock(&ue->card->user_ctl_lock);
} else { } else {
if (! ue->tlv_data_size || ! ue->tlv_data) int ret = 0;
return -ENXIO;
if (size < ue->tlv_data_size) mutex_lock(&ue->card->user_ctl_lock);
return -ENOSPC; if (!ue->tlv_data_size || !ue->tlv_data) {
ret = -ENXIO;
goto err_unlock;
}
if (size < ue->tlv_data_size) {
ret = -ENOSPC;
goto err_unlock;
}
if (copy_to_user(tlv, ue->tlv_data, ue->tlv_data_size)) if (copy_to_user(tlv, ue->tlv_data, ue->tlv_data_size))
return -EFAULT; ret = -EFAULT;
err_unlock:
mutex_unlock(&ue->card->user_ctl_lock);
if (ret)
return ret;
} }
return change; return change;
} }
...@@ -1136,8 +1165,6 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file, ...@@ -1136,8 +1165,6 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
struct user_element *ue; struct user_element *ue;
int idx, err; int idx, err;
if (!replace && card->user_ctl_count >= MAX_USER_CONTROLS)
return -ENOMEM;
if (info->count < 1) if (info->count < 1)
return -EINVAL; return -EINVAL;
access = info->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE : access = info->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE :
...@@ -1146,21 +1173,16 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file, ...@@ -1146,21 +1173,16 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE)); SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE));
info->id.numid = 0; info->id.numid = 0;
memset(&kctl, 0, sizeof(kctl)); memset(&kctl, 0, sizeof(kctl));
down_write(&card->controls_rwsem);
_kctl = snd_ctl_find_id(card, &info->id); if (replace) {
err = 0; err = snd_ctl_remove_user_ctl(file, &info->id);
if (_kctl) { if (err)
if (replace) return err;
err = snd_ctl_remove(card, _kctl);
else
err = -EBUSY;
} else {
if (replace)
err = -ENOENT;
} }
up_write(&card->controls_rwsem);
if (err < 0) if (card->user_ctl_count >= MAX_USER_CONTROLS)
return err; return -ENOMEM;
memcpy(&kctl.id, &info->id, sizeof(info->id)); memcpy(&kctl.id, &info->id, sizeof(info->id));
kctl.count = info->owner ? info->owner : 1; kctl.count = info->owner ? info->owner : 1;
access |= SNDRV_CTL_ELEM_ACCESS_USER; access |= SNDRV_CTL_ELEM_ACCESS_USER;
...@@ -1210,6 +1232,7 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file, ...@@ -1210,6 +1232,7 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
ue = kzalloc(sizeof(struct user_element) + private_size, GFP_KERNEL); ue = kzalloc(sizeof(struct user_element) + private_size, GFP_KERNEL);
if (ue == NULL) if (ue == NULL)
return -ENOMEM; return -ENOMEM;
ue->card = card;
ue->info = *info; ue->info = *info;
ue->info.access = 0; ue->info.access = 0;
ue->elem_data = (char *)ue + sizeof(*ue); ue->elem_data = (char *)ue + sizeof(*ue);
...@@ -1321,8 +1344,9 @@ static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file, ...@@ -1321,8 +1344,9 @@ static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file,
} }
err = kctl->tlv.c(kctl, op_flag, tlv.length, _tlv->tlv); err = kctl->tlv.c(kctl, op_flag, tlv.length, _tlv->tlv);
if (err > 0) { if (err > 0) {
struct snd_ctl_elem_id id = kctl->id;
up_read(&card->controls_rwsem); up_read(&card->controls_rwsem);
snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_TLV, &kctl->id); snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_TLV, &id);
return 0; return 0;
} }
} else { } else {
......
...@@ -232,6 +232,7 @@ int snd_card_new(struct device *parent, int idx, const char *xid, ...@@ -232,6 +232,7 @@ int snd_card_new(struct device *parent, int idx, const char *xid,
INIT_LIST_HEAD(&card->devices); INIT_LIST_HEAD(&card->devices);
init_rwsem(&card->controls_rwsem); init_rwsem(&card->controls_rwsem);
rwlock_init(&card->ctl_files_rwlock); rwlock_init(&card->ctl_files_rwlock);
mutex_init(&card->user_ctl_lock);
INIT_LIST_HEAD(&card->controls); INIT_LIST_HEAD(&card->controls);
INIT_LIST_HEAD(&card->ctl_files); INIT_LIST_HEAD(&card->ctl_files);
spin_lock_init(&card->files_lock); spin_lock_init(&card->files_lock);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment