diff options
Diffstat (limited to 'sound/core/control.c')
-rw-r--r-- | sound/core/control.c | 70 |
1 files changed, 25 insertions, 45 deletions
diff --git a/sound/core/control.c b/sound/core/control.c index 6362da17ac3f..3c6be1452e35 100644 --- a/sound/core/control.c +++ b/sound/core/control.c | |||
@@ -747,65 +747,45 @@ static int snd_ctl_card_info(struct snd_card *card, struct snd_ctl_file * ctl, | |||
747 | static int snd_ctl_elem_list(struct snd_card *card, | 747 | static int snd_ctl_elem_list(struct snd_card *card, |
748 | struct snd_ctl_elem_list __user *_list) | 748 | struct snd_ctl_elem_list __user *_list) |
749 | { | 749 | { |
750 | struct list_head *plist; | ||
751 | struct snd_ctl_elem_list list; | 750 | struct snd_ctl_elem_list list; |
752 | struct snd_kcontrol *kctl; | 751 | struct snd_kcontrol *kctl; |
753 | struct snd_ctl_elem_id *dst, *id; | 752 | struct snd_ctl_elem_id id; |
754 | unsigned int offset, space, jidx; | 753 | unsigned int offset, space, jidx; |
754 | int err = 0; | ||
755 | 755 | ||
756 | if (copy_from_user(&list, _list, sizeof(list))) | 756 | if (copy_from_user(&list, _list, sizeof(list))) |
757 | return -EFAULT; | 757 | return -EFAULT; |
758 | offset = list.offset; | 758 | offset = list.offset; |
759 | space = list.space; | 759 | space = list.space; |
760 | /* try limit maximum space */ | 760 | |
761 | if (space > 16384) | 761 | down_read(&card->controls_rwsem); |
762 | return -ENOMEM; | 762 | list.count = card->controls_count; |
763 | list.used = 0; | ||
763 | if (space > 0) { | 764 | if (space > 0) { |
764 | /* allocate temporary buffer for atomic operation */ | 765 | list_for_each_entry(kctl, &card->controls, list) { |
765 | dst = vmalloc(space * sizeof(struct snd_ctl_elem_id)); | 766 | if (offset >= kctl->count) { |
766 | if (dst == NULL) | 767 | offset -= kctl->count; |
767 | return -ENOMEM; | 768 | continue; |
768 | down_read(&card->controls_rwsem); | 769 | } |
769 | list.count = card->controls_count; | 770 | for (jidx = offset; jidx < kctl->count; jidx++) { |
770 | plist = card->controls.next; | 771 | snd_ctl_build_ioff(&id, kctl, jidx); |
771 | while (plist != &card->controls) { | 772 | if (copy_to_user(list.pids + list.used, &id, |
772 | if (offset == 0) | 773 | sizeof(id))) { |
773 | break; | 774 | err = -EFAULT; |
774 | kctl = snd_kcontrol(plist); | 775 | goto out; |
775 | if (offset < kctl->count) | 776 | } |
776 | break; | ||
777 | offset -= kctl->count; | ||
778 | plist = plist->next; | ||
779 | } | ||
780 | list.used = 0; | ||
781 | id = dst; | ||
782 | while (space > 0 && plist != &card->controls) { | ||
783 | kctl = snd_kcontrol(plist); | ||
784 | for (jidx = offset; space > 0 && jidx < kctl->count; jidx++) { | ||
785 | snd_ctl_build_ioff(id, kctl, jidx); | ||
786 | id++; | ||
787 | space--; | ||
788 | list.used++; | 777 | list.used++; |
778 | if (!--space) | ||
779 | goto out; | ||
789 | } | 780 | } |
790 | plist = plist->next; | ||
791 | offset = 0; | 781 | offset = 0; |
792 | } | 782 | } |
793 | up_read(&card->controls_rwsem); | ||
794 | if (list.used > 0 && | ||
795 | copy_to_user(list.pids, dst, | ||
796 | list.used * sizeof(struct snd_ctl_elem_id))) { | ||
797 | vfree(dst); | ||
798 | return -EFAULT; | ||
799 | } | ||
800 | vfree(dst); | ||
801 | } else { | ||
802 | down_read(&card->controls_rwsem); | ||
803 | list.count = card->controls_count; | ||
804 | up_read(&card->controls_rwsem); | ||
805 | } | 783 | } |
806 | if (copy_to_user(_list, &list, sizeof(list))) | 784 | out: |
807 | return -EFAULT; | 785 | up_read(&card->controls_rwsem); |
808 | return 0; | 786 | if (!err && copy_to_user(_list, &list, sizeof(list))) |
787 | err = -EFAULT; | ||
788 | return err; | ||
809 | } | 789 | } |
810 | 790 | ||
811 | static bool validate_element_member_dimension(struct snd_ctl_elem_info *info) | 791 | static bool validate_element_member_dimension(struct snd_ctl_elem_info *info) |