.../processor_thermal_device_pci.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-)
proc_thermal_pci_remove() cancels pci_info->work before disabling the
interrupt sources that schedule it. A threshold IRQ which arrives after
the cancel but before the THRES_0 and INT_ENABLE_0 MMIO mask makes
proc_thermal_irq_handler() reschedule the work, so
proc_thermal_threshold_work_fn() runs after devm cleanup has freed
pci_info and dereferences pci_info->tzone and proc_priv->mmio_base. The
shared IRQ is never freed in remove(), which widens the window, and the
probe error paths never cancel the work either.
Fix this by cutting off the producers before draining: mask the THRES_0
and INT_ENABLE_0 registers, free the MSI interrupts or the shared IRQ,
and only then cancel the delayed work. The probe error paths cancel the
work before the thermal zone is unregistered.
The work function re-enables the INT_ENABLE_0 bit, but the IRQs are
already freed when it is cancelled and THRES_0 stays cleared, so nothing
can schedule the work again.
This issue was found by an in-house static analysis tool.
Fixes: acd65d5d1cf4 ("thermal/drivers/int340x/processor_thermal: Add PCI MMIO based thermal driver")
Cc: stable@vger.kernel.org
Co-developed-by: Song Li <songl@zju.edu.cn>
Signed-off-by: Song Li <songl@zju.edu.cn>
Signed-off-by: Fan Wu <fanwu01@zju.edu.cn>
---
.../processor_thermal_device_pci.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c
index c693d93..1856e76 100644
--- a/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c
+++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c
@@ -409,7 +409,10 @@ static int proc_thermal_pci_probe(struct pci_dev *pdev, const struct pci_device_
err_free_vectors:
if (msi_irq)
proc_thermal_free_msi(pdev, pci_info);
+ else
+ devm_free_irq(&pdev->dev, pdev->irq, pci_info);
err_ret_tzone:
+ cancel_delayed_work_sync(&pci_info->work);
thermal_zone_device_unregister(pci_info->tzone);
err_del_legacy:
if (!pci_info->no_legacy)
@@ -424,13 +427,16 @@ static void proc_thermal_pci_remove(struct pci_dev *pdev)
struct proc_thermal_device *proc_priv = pci_get_drvdata(pdev);
struct proc_thermal_pci *pci_info = proc_priv->priv_data;
- cancel_delayed_work_sync(&pci_info->work);
-
proc_thermal_mmio_write(pci_info, PROC_THERMAL_MMIO_THRES_0, 0);
proc_thermal_mmio_write(pci_info, PROC_THERMAL_MMIO_INT_ENABLE_0, 0);
if (msi_irq)
proc_thermal_free_msi(pdev, pci_info);
+ else
+ devm_free_irq(&pdev->dev, pdev->irq, pci_info);
+
+ /* Cancel after the IRQs are freed, or the handler may reschedule it. */
+ cancel_delayed_work_sync(&pci_info->work);
thermal_zone_device_unregister(pci_info->tzone);
proc_thermal_mmio_remove(pdev, pci_info->proc_priv);
On Wed, 2026-09-23 at 07:27 +0000, Fan Wu wrote:
> proc_thermal_pci_remove() cancels pci_info->work before disabling the
> interrupt sources that schedule it. A threshold IRQ which arrives
> after
> the cancel but before the THRES_0 and INT_ENABLE_0 MMIO mask makes
> proc_thermal_irq_handler() reschedule the work, so
> proc_thermal_threshold_work_fn() runs after devm cleanup has freed
> pci_info and dereferences pci_info->tzone and proc_priv->mmio_base.
> The
> shared IRQ is never freed in remove(), which widens the window, and
> the
> probe error paths never cancel the work either.
>
> Fix this by cutting off the producers before draining: mask the
> THRES_0
> and INT_ENABLE_0 registers, free the MSI interrupts or the shared
> IRQ,
> and only then cancel the delayed work. The probe error paths cancel
> the
> work before the thermal zone is unregistered.
>
> The work function re-enables the INT_ENABLE_0 bit, but the IRQs are
> already freed when it is cancelled and THRES_0 stays cleared, so
> nothing
> can schedule the work again.
>
> This issue was found by an in-house static analysis tool.
>
> Fixes: acd65d5d1cf4 ("thermal/drivers/int340x/processor_thermal: Add
> PCI MMIO based thermal driver")
> Cc: stable@vger.kernel.org
> Co-developed-by: Song Li <songl@zju.edu.cn>
> Signed-off-by: Song Li <songl@zju.edu.cn>
> Signed-off-by: Fan Wu <fanwu01@zju.edu.cn>
Acked-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
> ---
> .../processor_thermal_device_pci.c | 10 ++++++++--
> 1 file changed, 8 insertions(+), 2 deletions(-)
>
> diff --git
> a/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.
> c
> b/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.
> c
> index c693d93..1856e76 100644
> ---
> a/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.
> c
> +++
> b/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.
> c
> @@ -409,7 +409,10 @@ static int proc_thermal_pci_probe(struct pci_dev
> *pdev, const struct pci_device_
> err_free_vectors:
> if (msi_irq)
> proc_thermal_free_msi(pdev, pci_info);
> + else
> + devm_free_irq(&pdev->dev, pdev->irq, pci_info);
> err_ret_tzone:
> + cancel_delayed_work_sync(&pci_info->work);
> thermal_zone_device_unregister(pci_info->tzone);
> err_del_legacy:
> if (!pci_info->no_legacy)
> @@ -424,13 +427,16 @@ static void proc_thermal_pci_remove(struct
> pci_dev *pdev)
> struct proc_thermal_device *proc_priv =
> pci_get_drvdata(pdev);
> struct proc_thermal_pci *pci_info = proc_priv->priv_data;
>
> - cancel_delayed_work_sync(&pci_info->work);
> -
> proc_thermal_mmio_write(pci_info, PROC_THERMAL_MMIO_THRES_0,
> 0);
> proc_thermal_mmio_write(pci_info,
> PROC_THERMAL_MMIO_INT_ENABLE_0, 0);
>
> if (msi_irq)
> proc_thermal_free_msi(pdev, pci_info);
> + else
> + devm_free_irq(&pdev->dev, pdev->irq, pci_info);
> +
> + /* Cancel after the IRQs are freed, or the handler may
> reschedule it. */
> + cancel_delayed_work_sync(&pci_info->work);
>
> thermal_zone_device_unregister(pci_info->tzone);
> proc_thermal_mmio_remove(pdev, pci_info->proc_priv);
© 2016 - 2026 Red Hat, Inc.