From 668160e5a80536251b4931a332dfe34d6ec2aeb7 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Thu, 19 Jul 2012 16:08:21 -0400 Subject: [PATCH] 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 CC: Oliver Neukum Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-core.c | 16 +++++++++++----- 1 file 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) 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;