aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hid
diff options
context:
space:
mode:
authorValentine Barshak <vbarshak@mvista.com>2010-12-06 09:51:41 -0500
committerJiri Kosina <jkosina@suse.cz>2010-12-07 09:45:49 -0500
commit1a8e8fab790ea7af81b8f964fdec706ad1ec2271 (patch)
tree21dcfd0e58d2d4369481b8d7dfa2d13e0263c40a /drivers/hid
parentce06b9d6d33fd2ed799b6e825d68fe95077da354 (diff)
HID: Fix race between disconnect and hiddev_ioctl
A USB HID device can be disconnected at any time. If this happens right before or while hiddev_ioctl is in progress, the hiddev_ioctl tries to access invalid hiddev->hid pointer. When the hid device is disconnected, the hiddev_disconnect() ends up with a call to hid_device_release() which frees hid_device, but doesn't set the hiddev->hid pointer to NULL. If the deallocated memory region has been re-used by the kernel, this can cause a crash or memory corruption. Since disconnect can happen at any time, we can't initialize struct hid_device *hid = hiddev->hid at the beginning of ioctl and then use it. This change checks hiddev->exist flag while holding the existancelock and uses hid_device only if it exists. Signed-off-by: Valentine Barshak <vbarshak@mvista.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'drivers/hid')
-rw-r--r--drivers/hid/usbhid/hiddev.c168
1 files changed, 131 insertions, 37 deletions
diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c
index fedd88df9a18..ffee7f205fd5 100644
--- a/drivers/hid/usbhid/hiddev.c
+++ b/drivers/hid/usbhid/hiddev.c
@@ -586,7 +586,7 @@ static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
586{ 586{
587 struct hiddev_list *list = file->private_data; 587 struct hiddev_list *list = file->private_data;
588 struct hiddev *hiddev = list->hiddev; 588 struct hiddev *hiddev = list->hiddev;
589 struct hid_device *hid = hiddev->hid; 589 struct hid_device *hid;
590 struct usb_device *dev; 590 struct usb_device *dev;
591 struct hiddev_collection_info cinfo; 591 struct hiddev_collection_info cinfo;
592 struct hiddev_report_info rinfo; 592 struct hiddev_report_info rinfo;
@@ -594,26 +594,33 @@ static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
594 struct hiddev_devinfo dinfo; 594 struct hiddev_devinfo dinfo;
595 struct hid_report *report; 595 struct hid_report *report;
596 struct hid_field *field; 596 struct hid_field *field;
597 struct usbhid_device *usbhid = hid->driver_data; 597 struct usbhid_device *usbhid;
598 void __user *user_arg = (void __user *)arg; 598 void __user *user_arg = (void __user *)arg;
599 int i, r; 599 int i, r;
600 600
601 /* Called without BKL by compat methods so no BKL taken */ 601 /* Called without BKL by compat methods so no BKL taken */
602 602
603 /* FIXME: Who or what stop this racing with a disconnect ?? */ 603 /* FIXME: Who or what stop this racing with a disconnect ?? */
604 if (!hiddev->exist || !hid) 604 if (!hiddev->exist)
605 return -EIO; 605 return -EIO;
606 606
607 dev = hid_to_usb_dev(hid);
608
609 switch (cmd) { 607 switch (cmd) {
610 608
611 case HIDIOCGVERSION: 609 case HIDIOCGVERSION:
612 return put_user(HID_VERSION, (int __user *)arg); 610 return put_user(HID_VERSION, (int __user *)arg);
613 611
614 case HIDIOCAPPLICATION: 612 case HIDIOCAPPLICATION:
615 if (arg < 0 || arg >= hid->maxapplication) 613 mutex_lock(&hiddev->existancelock);
616 return -EINVAL; 614 if (!hiddev->exist) {
615 r = -ENODEV;
616 goto ret_unlock;
617 }
618
619 hid = hiddev->hid;
620 if (arg < 0 || arg >= hid->maxapplication) {
621 r = -EINVAL;
622 goto ret_unlock;
623 }
617 624
618 for (i = 0; i < hid->maxcollection; i++) 625 for (i = 0; i < hid->maxcollection; i++)
619 if (hid->collection[i].type == 626 if (hid->collection[i].type ==
@@ -621,11 +628,22 @@ static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
621 break; 628 break;
622 629
623 if (i == hid->maxcollection) 630 if (i == hid->maxcollection)
624 return -EINVAL; 631 r = -EINVAL;
625 632 else
626 return hid->collection[i].usage; 633 r = hid->collection[i].usage;
634 goto ret_unlock;
627 635
628 case HIDIOCGDEVINFO: 636 case HIDIOCGDEVINFO:
637 mutex_lock(&hiddev->existancelock);
638 if (!hiddev->exist) {
639 r = -ENODEV;
640 goto ret_unlock;
641 }
642
643 hid = hiddev->hid;
644 dev = hid_to_usb_dev(hid);
645 usbhid = hid->driver_data;
646
629 dinfo.bustype = BUS_USB; 647 dinfo.bustype = BUS_USB;
630 dinfo.busnum = dev->bus->busnum; 648 dinfo.busnum = dev->bus->busnum;
631 dinfo.devnum = dev->devnum; 649 dinfo.devnum = dev->devnum;
@@ -634,6 +652,8 @@ static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
634 dinfo.product = le16_to_cpu(dev->descriptor.idProduct); 652 dinfo.product = le16_to_cpu(dev->descriptor.idProduct);
635 dinfo.version = le16_to_cpu(dev->descriptor.bcdDevice); 653 dinfo.version = le16_to_cpu(dev->descriptor.bcdDevice);
636 dinfo.num_applications = hid->maxapplication; 654 dinfo.num_applications = hid->maxapplication;
655 mutex_unlock(&hiddev->existancelock);
656
637 if (copy_to_user(user_arg, &dinfo, sizeof(dinfo))) 657 if (copy_to_user(user_arg, &dinfo, sizeof(dinfo)))
638 return -EFAULT; 658 return -EFAULT;
639 659
@@ -667,6 +687,7 @@ static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
667 r = hiddev_ioctl_string(hiddev, cmd, user_arg); 687 r = hiddev_ioctl_string(hiddev, cmd, user_arg);
668 else 688 else
669 r = -ENODEV; 689 r = -ENODEV;
690ret_unlock:
670 mutex_unlock(&hiddev->existancelock); 691 mutex_unlock(&hiddev->existancelock);
671 return r; 692 return r;
672 693
@@ -676,6 +697,7 @@ static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
676 mutex_unlock(&hiddev->existancelock); 697 mutex_unlock(&hiddev->existancelock);
677 return -ENODEV; 698 return -ENODEV;
678 } 699 }
700 hid = hiddev->hid;
679 usbhid_init_reports(hid); 701 usbhid_init_reports(hid);
680 mutex_unlock(&hiddev->existancelock); 702 mutex_unlock(&hiddev->existancelock);
681 703
@@ -688,14 +710,21 @@ static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
688 if (rinfo.report_type == HID_REPORT_TYPE_OUTPUT) 710 if (rinfo.report_type == HID_REPORT_TYPE_OUTPUT)
689 return -EINVAL; 711 return -EINVAL;
690 712
691 if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
692 return -EINVAL;
693
694 mutex_lock(&hiddev->existancelock); 713 mutex_lock(&hiddev->existancelock);
695 if (hiddev->exist) { 714 if (!hiddev->exist) {
696 usbhid_submit_report(hid, report, USB_DIR_IN); 715 r = -ENODEV;
697 usbhid_wait_io(hid); 716 goto ret_unlock;
717 }
718
719 hid = hiddev->hid;
720 report = hiddev_lookup_report(hid, &rinfo);
721 if (report == NULL) {
722 r = -EINVAL;
723 goto ret_unlock;
698 } 724 }
725
726 usbhid_submit_report(hid, report, USB_DIR_IN);
727 usbhid_wait_io(hid);
699 mutex_unlock(&hiddev->existancelock); 728 mutex_unlock(&hiddev->existancelock);
700 729
701 return 0; 730 return 0;
@@ -707,14 +736,21 @@ static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
707 if (rinfo.report_type == HID_REPORT_TYPE_INPUT) 736 if (rinfo.report_type == HID_REPORT_TYPE_INPUT)
708 return -EINVAL; 737 return -EINVAL;
709 738
710 if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
711 return -EINVAL;
712
713 mutex_lock(&hiddev->existancelock); 739 mutex_lock(&hiddev->existancelock);
714 if (hiddev->exist) { 740 if (!hiddev->exist) {
715 usbhid_submit_report(hid, report, USB_DIR_OUT); 741 r = -ENODEV;
716 usbhid_wait_io(hid); 742 goto ret_unlock;
743 }
744
745 hid = hiddev->hid;
746 report = hiddev_lookup_report(hid, &rinfo);
747 if (report == NULL) {
748 r = -EINVAL;
749 goto ret_unlock;
717 } 750 }
751
752 usbhid_submit_report(hid, report, USB_DIR_OUT);
753 usbhid_wait_io(hid);
718 mutex_unlock(&hiddev->existancelock); 754 mutex_unlock(&hiddev->existancelock);
719 755
720 return 0; 756 return 0;
@@ -723,10 +759,21 @@ static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
723 if (copy_from_user(&rinfo, user_arg, sizeof(rinfo))) 759 if (copy_from_user(&rinfo, user_arg, sizeof(rinfo)))
724 return -EFAULT; 760 return -EFAULT;
725 761
726 if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL) 762 mutex_lock(&hiddev->existancelock);
727 return -EINVAL; 763 if (!hiddev->exist) {
764 r = -ENODEV;
765 goto ret_unlock;
766 }
767
768 hid = hiddev->hid;
769 report = hiddev_lookup_report(hid, &rinfo);
770 if (report == NULL) {
771 r = -EINVAL;
772 goto ret_unlock;
773 }
728 774
729 rinfo.num_fields = report->maxfield; 775 rinfo.num_fields = report->maxfield;
776 mutex_unlock(&hiddev->existancelock);
730 777
731 if (copy_to_user(user_arg, &rinfo, sizeof(rinfo))) 778 if (copy_to_user(user_arg, &rinfo, sizeof(rinfo)))
732 return -EFAULT; 779 return -EFAULT;
@@ -738,11 +785,23 @@ static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
738 return -EFAULT; 785 return -EFAULT;
739 rinfo.report_type = finfo.report_type; 786 rinfo.report_type = finfo.report_type;
740 rinfo.report_id = finfo.report_id; 787 rinfo.report_id = finfo.report_id;
741 if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL) 788 mutex_lock(&hiddev->existancelock);
742 return -EINVAL; 789 if (!hiddev->exist) {
790 r = -ENODEV;
791 goto ret_unlock;
792 }
743 793
744 if (finfo.field_index >= report->maxfield) 794 hid = hiddev->hid;
745 return -EINVAL; 795 report = hiddev_lookup_report(hid, &rinfo);
796 if (report == NULL) {
797 r = -EINVAL;
798 goto ret_unlock;
799 }
800
801 if (finfo.field_index >= report->maxfield) {
802 r = -EINVAL;
803 goto ret_unlock;
804 }
746 805
747 field = report->field[finfo.field_index]; 806 field = report->field[finfo.field_index];
748 memset(&finfo, 0, sizeof(finfo)); 807 memset(&finfo, 0, sizeof(finfo));
@@ -760,6 +819,7 @@ static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
760 finfo.physical_maximum = field->physical_maximum; 819 finfo.physical_maximum = field->physical_maximum;
761 finfo.unit_exponent = field->unit_exponent; 820 finfo.unit_exponent = field->unit_exponent;
762 finfo.unit = field->unit; 821 finfo.unit = field->unit;
822 mutex_unlock(&hiddev->existancelock);
763 823
764 if (copy_to_user(user_arg, &finfo, sizeof(finfo))) 824 if (copy_to_user(user_arg, &finfo, sizeof(finfo)))
765 return -EFAULT; 825 return -EFAULT;
@@ -785,12 +845,22 @@ static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
785 if (copy_from_user(&cinfo, user_arg, sizeof(cinfo))) 845 if (copy_from_user(&cinfo, user_arg, sizeof(cinfo)))
786 return -EFAULT; 846 return -EFAULT;
787 847
788 if (cinfo.index >= hid->maxcollection) 848 mutex_lock(&hiddev->existancelock);
789 return -EINVAL; 849 if (!hiddev->exist) {
850 r = -ENODEV;
851 goto ret_unlock;
852 }
853
854 hid = hiddev->hid;
855 if (cinfo.index >= hid->maxcollection) {
856 r = -EINVAL;
857 goto ret_unlock;
858 }
790 859
791 cinfo.type = hid->collection[cinfo.index].type; 860 cinfo.type = hid->collection[cinfo.index].type;
792 cinfo.usage = hid->collection[cinfo.index].usage; 861 cinfo.usage = hid->collection[cinfo.index].usage;
793 cinfo.level = hid->collection[cinfo.index].level; 862 cinfo.level = hid->collection[cinfo.index].level;
863 mutex_lock(&hiddev->existancelock);
794 864
795 if (copy_to_user(user_arg, &cinfo, sizeof(cinfo))) 865 if (copy_to_user(user_arg, &cinfo, sizeof(cinfo)))
796 return -EFAULT; 866 return -EFAULT;
@@ -803,24 +873,48 @@ static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
803 873
804 if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGNAME(0))) { 874 if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGNAME(0))) {
805 int len; 875 int len;
806 if (!hid->name) 876
807 return 0; 877 mutex_lock(&hiddev->existancelock);
878 if (!hiddev->exist) {
879 r = -ENODEV;
880 goto ret_unlock;
881 }
882
883 hid = hiddev->hid;
884 if (!hid->name) {
885 r = 0;
886 goto ret_unlock;
887 }
888
808 len = strlen(hid->name) + 1; 889 len = strlen(hid->name) + 1;
809 if (len > _IOC_SIZE(cmd)) 890 if (len > _IOC_SIZE(cmd))
810 len = _IOC_SIZE(cmd); 891 len = _IOC_SIZE(cmd);
811 return copy_to_user(user_arg, hid->name, len) ? 892 r = copy_to_user(user_arg, hid->name, len) ?
812 -EFAULT : len; 893 -EFAULT : len;
894 goto ret_unlock;
813 } 895 }
814 896
815 if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGPHYS(0))) { 897 if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGPHYS(0))) {
816 int len; 898 int len;
817 if (!hid->phys) 899
818 return 0; 900 mutex_lock(&hiddev->existancelock);
901 if (!hiddev->exist) {
902 r = -ENODEV;
903 goto ret_unlock;
904 }
905
906 hid = hiddev->hid;
907 if (!hid->phys) {
908 r = 0;
909 goto ret_unlock;
910 }
911
819 len = strlen(hid->phys) + 1; 912 len = strlen(hid->phys) + 1;
820 if (len > _IOC_SIZE(cmd)) 913 if (len > _IOC_SIZE(cmd))
821 len = _IOC_SIZE(cmd); 914 len = _IOC_SIZE(cmd);
822 return copy_to_user(user_arg, hid->phys, len) ? 915 r = copy_to_user(user_arg, hid->phys, len) ?
823 -EFAULT : len; 916 -EFAULT : len;
917 goto ret_unlock;
824 } 918 }
825 } 919 }
826 return -EINVAL; 920 return -EINVAL;