Re: [PATCH v5 2/3] HID: Intel-thc-hid: Intel-quicki2c: Refine recover callback

[email protected] Wed, 29 Jul 2026 05:17:59 +0000
Newsgroups org.kernel.vger.linux-input
Message-ID <[email protected]>
Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:

New issues:
- [High] Use-After-Free and race condition during device teardown due to improper workqueue cancellation.
- [Medium] Failed suspend operation leaves the device recovery mechanism permanently disabled.

Pre-existing issues:
- [High] Missing cleanup of IRQ vectors on error paths and device removal, leading to an IRQ resource leak.
- [High] Unsafe MMIO register access in hard IRQ handler without PM runtime verification.
- [High] Use-After-Free on the HID device structure during removal.
--

--- Patch [2]: [PATCH v5 2/3] HID: Intel-thc-hid: Intel-quicki2c: Refine recover callback ---
commit f51d43428a895d224d1667aba97de68a9108ddc3
Author: Even Xu <[email protected]>

HID: Intel-thc-hid: Intel-quicki2c: Refine recover callback

This commit introduces a workqueue for the recovery flow in the quicki2c
driver instead of processing it in the IRQ handler. It also simplifies
recovery using the thc_rxdma_reset() API and disables interrupts during
the whole recover flow to prevent storms on failure.

> diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c b/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c
> index 46d3e9a019995..539da002aa4fd 100644
> --- a/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c
> +++ b/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c
> @@ -245,28 +245,33 @@ static irqreturn_t quicki2c_irq_quick_handler(int irq, void *dev_id)
>  }

[Severity: High]
This is a pre-existing issue, but could this cause an unsafe MMIO access?

Looking at quicki2c_irq_quick_handler():

static irqreturn_t quicki2c_irq_quick_handler(int irq, void *dev_id)
{
    ...
    /* Disable THC interrupt before current interrupt be handled */
    thc_interrupt_enable(qcdev->thc_hw, false);
    ...
}

The probe function allows fallback to shared legacy INTx interrupts via
pci_alloc_irq_vectors(). If the device is in D3hot (runtime suspended) and
a shared interrupt triggers on the same line, could this handler access the
thc_interrupt_enable MMIO register on powered-down hardware?

PM subsystem guidelines suggest checking pm_runtime_get_if_active() before
register access in shared IRQ handlers.

[ ... ]

> @@ -439,6 +445,9 @@ static struct quicki2c_device *quicki2c_dev_init(struct pci_dev *pdev, void __io
>   */
>  static void quicki2c_dev_deinit(struct quicki2c_device *qcdev)
>  {
> +	WRITE_ONCE(qcdev->recovery_disabled, true);
> +	cancel_work_sync(&qcdev->recover_work);
> +
>  	thc_interrupt_quiesce(qcdev->thc_hw, true);
>  	thc_interrupt_enable(qcdev->thc_hw, false);

[Severity: High]
Is there a race condition here that could lead to a use-after-free?

Because cancel_work_sync() is called before the hardware interrupt source
is disabled with thc_interrupt_enable(), could a concurrent or preempted
threaded IRQ handler call schedule_work() immediately after
cancel_work_sync() returns?

If the work is placed back on the queue and the qcdev structure is
subsequently freed during removal, would the workqueue eventually execute
try_recover() on freed memory?

[ ... ]

> @@ -772,12 +781,13 @@ static void quicki2c_remove(struct pci_dev *pdev)
>  		return;
>  
>  	quicki2c_hid_remove(qcdev);
> +
> +	quicki2c_dev_deinit(qcdev);

[Severity: High]
This is a pre-existing issue, but does destroying the HID device before
disabling hardware interrupts create a race window?

If an RX interrupt arrives after quicki2c_hid_remove() completes but before
quicki2c_dev_deinit() disables interrupts, could the threaded IRQ handler
execute handle_input_report() and invoke quicki2c_hid_send_report(),
leading to a dereference of the freed qcdev->hid_dev pointer?

[Severity: High]
This is also a pre-existing issue, but are the IRQ vectors leaked
during removal?

The probe function allocates vectors via pci_alloc_irq_vectors() with
PCI_IRQ_ALL_TYPES, but there does not appear to be a corresponding
pci_free_irq_vectors() in quicki2c_remove() or the probe error paths.

Does this result in a permanent leak of system IRQ resources?

>  	quicki2c_dma_deinit(qcdev);
>  
>  	pm_runtime_get_noresume(qcdev->dev);
>  
> -	quicki2c_dev_deinit(qcdev);
> -
>  	pci_clear_master(pdev);
>  }

[ ... ]

> @@ -826,6 +836,9 @@ static int quicki2c_suspend(struct device *device)
>  	if (ret)
>  		return ret;
>  
> +	WRITE_ONCE(qcdev->recovery_disabled, true);
> +	cancel_work_sync(&qcdev->recover_work);
> +
>  	ret = thc_interrupt_quiesce(qcdev->thc_hw, true);
>  	if (ret)
>  		return ret;

[Severity: Medium]
If thc_interrupt_quiesce() fails and returns an error here, is
qcdev->recovery_disabled left permanently set to true?

Since a failed suspend aborts the transition and won't trigger a subsequent
resume callback to reset the flag, would this leave the driver in a state
where try_recover() silently aborts on all future errors?

-- 
Sashiko AI review ยท https://sashiko.dev/#/patchset/[email protected]?part=2