diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2012-07-19 16:08:21 -0400 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2012-07-20 05:24:23 -0400 |
commit | 668160e5a80536251b4931a332dfe34d6ec2aeb7 (patch) | |
tree | 326dff1f73e54f8f57a49b090a9f0411cd22dfdb | |
parent | 61c901c56905256a4a4d7c2af92d66200a2ee7f2 (diff) |
HID: usbhid: fix use-after-free bug
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>
-rw-r--r-- | drivers/hid/usbhid/hid-core.c | 16 |
1 files changed, 11 insertions, 5 deletions
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 482f936fc29b..6b9bad540702 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c | |||
@@ -331,9 +331,12 @@ static int hid_submit_out(struct hid_device *hid) | |||
331 | usbhid->urbout->transfer_buffer_length = ((report->size - 1) >> 3) + | 331 | usbhid->urbout->transfer_buffer_length = ((report->size - 1) >> 3) + |
332 | 1 + (report->id > 0); | 332 | 1 + (report->id > 0); |
333 | usbhid->urbout->dev = hid_to_usb_dev(hid); | 333 | usbhid->urbout->dev = hid_to_usb_dev(hid); |
334 | memcpy(usbhid->outbuf, raw_report, | 334 | if (raw_report) { |
335 | usbhid->urbout->transfer_buffer_length); | 335 | memcpy(usbhid->outbuf, raw_report, |
336 | kfree(raw_report); | 336 | usbhid->urbout->transfer_buffer_length); |
337 | kfree(raw_report); | ||
338 | usbhid->out[usbhid->outtail].raw_report = NULL; | ||
339 | } | ||
337 | 340 | ||
338 | dbg_hid("submitting out urb\n"); | 341 | dbg_hid("submitting out urb\n"); |
339 | 342 | ||
@@ -362,8 +365,11 @@ static int hid_submit_ctrl(struct hid_device *hid) | |||
362 | if (dir == USB_DIR_OUT) { | 365 | if (dir == USB_DIR_OUT) { |
363 | usbhid->urbctrl->pipe = usb_sndctrlpipe(hid_to_usb_dev(hid), 0); | 366 | usbhid->urbctrl->pipe = usb_sndctrlpipe(hid_to_usb_dev(hid), 0); |
364 | usbhid->urbctrl->transfer_buffer_length = len; | 367 | usbhid->urbctrl->transfer_buffer_length = len; |
365 | memcpy(usbhid->ctrlbuf, raw_report, len); | 368 | if (raw_report) { |
366 | kfree(raw_report); | 369 | memcpy(usbhid->ctrlbuf, raw_report, len); |
370 | kfree(raw_report); | ||
371 | usbhid->ctrl[usbhid->ctrltail].raw_report = NULL; | ||
372 | } | ||
367 | } else { | 373 | } else { |
368 | int maxpacket, padlen; | 374 | int maxpacket, padlen; |
369 | 375 | ||