Re: [PATCH v2 2/2] HID: usbhid: free unsent raw output reports in usbhid_stop()

[email protected]
Newsgroups org.kernel.vger.linux-input
Message-ID <[email protected]>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Emptying the `out` queue in `usbhid_stop` without updating the corresponding `hid_irq_out` completion handler causes an out-of-bounds queue access, leading to a NULL pointer dereference and kernel panic.
--

--- Patch [2]: [PATCH v2 2/2] HID: usbhid: free unsent raw output reports in usbhid_stop() ---
commit 026e2d1c35a0ae9588e9e7e4b956512ff4689607
Author: Dmitry Antipov <[email protected]>

HID: usbhid: free unsent raw output reports in usbhid_stop()

When a HID device is stalled or disconnected, there might be unsent
output reports. This patch frees their raw buffers in usbhid_stop()
to avoid memory leaks.

Closes: https://syzkaller.appspot.com/bug?extid=e2c057ea576d2644e2be

> diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
> index 96b0181cf8194..60662616fd2a0 100644
> --- a/drivers/hid/usbhid/hid-core.c
> +++ b/drivers/hid/usbhid/hid-core.c
> @@ -1253,6 +1253,14 @@ static void usbhid_stop(struct hid_device *hid)
>  		usbhid->ctrltail = (usbhid->ctrltail + 1) &
>  			(HID_CONTROL_FIFO_SIZE - 1);
>  	}
> +
> +	while (usbhid->outtail != usbhid->outhead) {
> +		kfree(usbhid->out[usbhid->outtail].raw_report);
> +		usbhid->out[usbhid->outtail].raw_report = NULL;
> +
> +		usbhid->outtail = (usbhid->outtail + 1) &
> +			(HID_OUTPUT_FIFO_SIZE - 1);
> +	}

[Severity: High]
Could emptying the out queue here cause a NULL pointer dereference if an
output URB is currently in-flight?

If an in-flight URB is canceled by the subsequent usb_kill_urb() call, it
will synchronously trigger the completion handler hid_irq_out() with status
-ENOENT.

Unlike hid_ctrl(), hid_irq_out() unconditionally increments outtail:

drivers/hid/usbhid/hid-core.c:hid_irq_out() {
    ...
    } else {
        usbhid->outtail = (usbhid->outtail + 1) & (HID_OUTPUT_FIFO_SIZE - 1);

        if (usbhid->outhead != usbhid->outtail &&
                hid_submit_out(hid) == 0) {
    ...
}

If usbhid_stop() has already made outtail equal to outhead, this increment
pushes outtail out of bounds. The outhead != outtail check will then pass,
and it will call hid_submit_out() which will dereference a NULL report:

drivers/hid/usbhid/hid-core.c:hid_submit_out() {
    ...
    report = usbhid->out[usbhid->outtail].report;
    usbhid->urbout->transfer_buffer_length = hid_report_len(report);
    ...
}

>  	spin_unlock_irq(&usbhid->lock);
>  
>  	usb_kill_urb(usbhid->urbin);

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=2
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.