Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

HID: fix oops during suspend of unbound HID devices

Usbhid structure is allocated on start invoked only from probe
of some driver. When there is no driver, the structure is null
and causes null-dereference oopses.

Fix it by allocating the structure on probe and disconnect of
the device itself. Also make sure we won't race between start
and resume or stop and suspend respectively.

References: http://bugzilla.kernel.org/show_bug.cgi?id=11827

Signed-off-by: Jiri Slaby <jirislaby@gmail.com>
Cc: Johannes Berg <johannes@sipsolutions.net>
Cc: Andreas Schwab <schwab@suse.de>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>

authored by

Jiri Slaby and committed by
Jiri Kosina
3d5afd32 f8d56f17

+44 -17
+41 -17
drivers/hid/usbhid/hid-core.c
··· 20 20 #include <linux/kernel.h> 21 21 #include <linux/list.h> 22 22 #include <linux/mm.h> 23 + #include <linux/mutex.h> 23 24 #include <linux/smp_lock.h> 24 25 #include <linux/spinlock.h> 25 26 #include <asm/unaligned.h> ··· 777 776 struct usb_interface *intf = to_usb_interface(hid->dev.parent); 778 777 struct usb_host_interface *interface = intf->cur_altsetting; 779 778 struct usb_device *dev = interface_to_usbdev(intf); 780 - struct usbhid_device *usbhid; 779 + struct usbhid_device *usbhid = hid->driver_data; 781 780 unsigned int n, insize = 0; 782 781 int ret; 783 - 784 - WARN_ON(hid->driver_data); 785 - 786 - usbhid = kzalloc(sizeof(struct usbhid_device), GFP_KERNEL); 787 - if (usbhid == NULL) { 788 - ret = -ENOMEM; 789 - goto err; 790 - } 791 - 792 - hid->driver_data = usbhid; 793 - usbhid->hid = hid; 794 782 795 783 usbhid->bufsize = HID_MIN_BUFFER_SIZE; 796 784 hid_find_max_report(hid, HID_INPUT_REPORT, &usbhid->bufsize); ··· 794 804 if (insize > HID_MAX_BUFFER_SIZE) 795 805 insize = HID_MAX_BUFFER_SIZE; 796 806 807 + mutex_lock(&usbhid->setup); 797 808 if (hid_alloc_buffers(dev, hid)) { 798 809 ret = -ENOMEM; 799 810 goto fail; ··· 879 888 usbhid_init_reports(hid); 880 889 hid_dump_device(hid); 881 890 891 + set_bit(HID_STARTED, &usbhid->iofl); 892 + mutex_unlock(&usbhid->setup); 893 + 882 894 return 0; 883 895 884 896 fail: ··· 889 895 usb_free_urb(usbhid->urbout); 890 896 usb_free_urb(usbhid->urbctrl); 891 897 hid_free_buffers(dev, hid); 892 - kfree(usbhid); 893 - err: 898 + mutex_unlock(&usbhid->setup); 894 899 return ret; 895 900 } 896 901 ··· 900 907 if (WARN_ON(!usbhid)) 901 908 return; 902 909 910 + mutex_lock(&usbhid->setup); 911 + clear_bit(HID_STARTED, &usbhid->iofl); 903 912 spin_lock_irq(&usbhid->inlock); /* Sync with error handler */ 904 913 set_bit(HID_DISCONNECTED, &usbhid->iofl); 905 914 spin_unlock_irq(&usbhid->inlock); ··· 926 931 usb_free_urb(usbhid->urbout); 927 932 928 933 hid_free_buffers(hid_to_usb_dev(hid), hid); 929 - kfree(usbhid); 930 - hid->driver_data = NULL; 934 + mutex_unlock(&usbhid->setup); 931 935 } 932 936 933 937 static struct hid_ll_driver usb_hid_driver = { ··· 941 947 static int hid_probe(struct usb_interface *intf, const struct usb_device_id *id) 942 948 { 943 949 struct usb_device *dev = interface_to_usbdev(intf); 950 + struct usbhid_device *usbhid; 944 951 struct hid_device *hid; 945 952 size_t len; 946 953 int ret; ··· 995 1000 if (usb_string(dev, dev->descriptor.iSerialNumber, hid->uniq, 64) <= 0) 996 1001 hid->uniq[0] = 0; 997 1002 1003 + usbhid = kzalloc(sizeof(*usbhid), GFP_KERNEL); 1004 + if (usbhid == NULL) { 1005 + ret = -ENOMEM; 1006 + goto err; 1007 + } 1008 + 1009 + hid->driver_data = usbhid; 1010 + usbhid->hid = hid; 1011 + mutex_init(&usbhid->setup); /* needed on suspend/resume */ 1012 + 998 1013 ret = hid_add_device(hid); 999 1014 if (ret) { 1000 1015 if (ret != -ENODEV) 1001 1016 dev_err(&intf->dev, "can't add hid device: %d\n", ret); 1002 - goto err; 1017 + goto err_free; 1003 1018 } 1004 1019 1005 1020 return 0; 1021 + err_free: 1022 + kfree(usbhid); 1006 1023 err: 1007 1024 hid_destroy_device(hid); 1008 1025 return ret; ··· 1023 1016 static void hid_disconnect(struct usb_interface *intf) 1024 1017 { 1025 1018 struct hid_device *hid = usb_get_intfdata(intf); 1019 + struct usbhid_device *usbhid; 1026 1020 1027 1021 if (WARN_ON(!hid)) 1028 1022 return; 1029 1023 1024 + usbhid = hid->driver_data; 1030 1025 hid_destroy_device(hid); 1026 + kfree(usbhid); 1031 1027 } 1032 1028 1033 1029 static int hid_suspend(struct usb_interface *intf, pm_message_t message) ··· 1038 1028 struct hid_device *hid = usb_get_intfdata (intf); 1039 1029 struct usbhid_device *usbhid = hid->driver_data; 1040 1030 1031 + mutex_lock(&usbhid->setup); 1032 + if (!test_bit(HID_STARTED, &usbhid->iofl)) { 1033 + mutex_unlock(&usbhid->setup); 1034 + return 0; 1035 + } 1036 + 1041 1037 spin_lock_irq(&usbhid->inlock); /* Sync with error handler */ 1042 1038 set_bit(HID_SUSPENDED, &usbhid->iofl); 1043 1039 spin_unlock_irq(&usbhid->inlock); 1044 1040 del_timer(&usbhid->io_retry); 1045 1041 usb_kill_urb(usbhid->urbin); 1042 + mutex_unlock(&usbhid->setup); 1046 1043 dev_dbg(&intf->dev, "suspend\n"); 1047 1044 return 0; 1048 1045 } ··· 1060 1043 struct usbhid_device *usbhid = hid->driver_data; 1061 1044 int status; 1062 1045 1046 + mutex_lock(&usbhid->setup); 1047 + if (!test_bit(HID_STARTED, &usbhid->iofl)) { 1048 + mutex_unlock(&usbhid->setup); 1049 + return 0; 1050 + } 1051 + 1063 1052 clear_bit(HID_SUSPENDED, &usbhid->iofl); 1064 1053 usbhid->retry_delay = 0; 1065 1054 status = hid_start_in(hid); 1055 + mutex_unlock(&usbhid->setup); 1066 1056 dev_dbg(&intf->dev, "resume status %d\n", status); 1067 1057 return status; 1068 1058 }
+2
drivers/hid/usbhid/usbhid.h
··· 27 27 #include <linux/types.h> 28 28 #include <linux/slab.h> 29 29 #include <linux/list.h> 30 + #include <linux/mutex.h> 30 31 #include <linux/timer.h> 31 32 #include <linux/wait.h> 32 33 #include <linux/workqueue.h> ··· 74 73 dma_addr_t outbuf_dma; /* Output buffer dma */ 75 74 spinlock_t outlock; /* Output fifo spinlock */ 76 75 76 + struct mutex setup; 77 77 unsigned long iofl; /* I/O flags (CTRL_RUNNING, OUT_RUNNING) */ 78 78 struct timer_list io_retry; /* Retry timer */ 79 79 unsigned long stop_retry; /* Time to give up, in jiffies */
+1
include/linux/hid.h
··· 410 410 #define HID_SUSPENDED 5 411 411 #define HID_CLEAR_HALT 6 412 412 #define HID_DISCONNECTED 7 413 + #define HID_STARTED 8 413 414 414 415 struct hid_input { 415 416 struct list_head list;