HID: usbhid: fix use-after-free bug
authorAlan Stern <stern@rowland.harvard.edu>
Thu, 19 Jul 2012 20:08:21 +0000 (16:08 -0400)
committerJiri Kosina <jkosina@suse.cz>
Fri, 20 Jul 2012 09:24:23 +0000 (11:24 +0200)
This patch (as1592) fixes an obscure problem in the usbhid driver.
Under some circumstances, a control or interrupt-OUT URB can be
submitted twice.  This will happen if the first submission fails; the
queue pointers aren't updated, so the next time the queue is restarted
the same URB will be submitted again.

The problem is that raw_report gets deallocated during the first
submission.  The second submission will then dereference and try to
free an already-freed region of memory.  The patch fixes the problem
by setting raw_report to NULL when it is deallocated and checking for
NULL before dereferencing it.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
CC: Oliver Neukum <oliver@neukum.org>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
drivers/hid/usbhid/hid-core.c

index 482f936fc29b0deb04b3ddb3b7fe894a60c95b57..6b9bad54070239bcf22602fd066e19adb0ac822e 100644 (file)
@@ -331,9 +331,12 @@ static int hid_submit_out(struct hid_device *hid)
        usbhid->urbout->transfer_buffer_length = ((report->size - 1) >> 3) +
                                                 1 + (report->id > 0);
        usbhid->urbout->dev = hid_to_usb_dev(hid);
-       memcpy(usbhid->outbuf, raw_report,
-              usbhid->urbout->transfer_buffer_length);
-       kfree(raw_report);
+       if (raw_report) {
+               memcpy(usbhid->outbuf, raw_report,
+                               usbhid->urbout->transfer_buffer_length);
+               kfree(raw_report);
+               usbhid->out[usbhid->outtail].raw_report = NULL;
+       }
 
        dbg_hid("submitting out urb\n");
 
@@ -362,8 +365,11 @@ static int hid_submit_ctrl(struct hid_device *hid)
        if (dir == USB_DIR_OUT) {
                usbhid->urbctrl->pipe = usb_sndctrlpipe(hid_to_usb_dev(hid), 0);
                usbhid->urbctrl->transfer_buffer_length = len;
-               memcpy(usbhid->ctrlbuf, raw_report, len);
-               kfree(raw_report);
+               if (raw_report) {
+                       memcpy(usbhid->ctrlbuf, raw_report, len);
+                       kfree(raw_report);
+                       usbhid->ctrl[usbhid->ctrltail].raw_report = NULL;
+               }
        } else {
                int maxpacket, padlen;