From aaa6c8dec49339710f9fd2a2af5646cf4111dbd4 Mon Sep 17 00:00:00 2001 From: Weigang He Date: Sat, 26 Sep 2026 22:49:31 +1000 Subject: [PATCH] thermal/drivers/ti-soc-thermal: Cancel pending alert work on remove On a threshold crossing, the talert IRQ handler calls ti_thermal_report_sensor_temperature(), which queues the work embedded in the sensor's ti_thermal_data on the system workqueue. Nothing ever cancels or flushes that work. ti_bandgap_remove() frees the talert IRQ last. free_irq() waits for a running handler, but not for the work the handler has queued. When remove returns, devres unregisters and frees the thermal zones and then frees the devm-allocated ti_thermal_data, so a work item that is still pending runs ti_thermal_work() on freed memory: ti_thermal_work() data = container_of(work, struct ti_thermal_data, thermal_wq); thermal_zone_device_update(data->ti_thermal, ...); Free the talert IRQ before removing the sensors, so that no new work can be queued, and cancel the work in ti_thermal_remove_sensor(). This needs an OMAP4460/4470 or OMAP5 SoC (DRA7 has TALERT but no ->report_temperature, so it never queues the work), a threshold crossing just before the driver is unbound or unloaded, and the work still pending when devres frees the data. Found by static analysis tool CodeQL. Fixes: 445eaf871bf9 ("staging: omap-thermal: common code to expose driver to thermal framework") Assisted-by: LLM codeql Signed-off-by: Weigang He Signed-off-by: Daniel Lezcano Acked-by: Keerthy Link: https://patch.msgid.link/20260926124931.3599746-1-geoffreyhe2@gmail.com --- drivers/thermal/ti-soc-thermal/ti-bandgap.c | 7 ++++--- drivers/thermal/ti-soc-thermal/ti-thermal-common.c | 4 ++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/thermal/ti-soc-thermal/ti-bandgap.c b/drivers/thermal/ti-soc-thermal/ti-bandgap.c index ba43399d0b38..507fe67eaffa 100644 --- a/drivers/thermal/ti-soc-thermal/ti-bandgap.c +++ b/drivers/thermal/ti-soc-thermal/ti-bandgap.c @@ -1077,6 +1077,10 @@ void ti_bandgap_remove(struct platform_device *pdev) if (!soc_device_match(soc_no_cpu_notifier)) cpu_pm_unregister_notifier(&bgp->nb); + /* Stop the alerts first: they queue work on the sensors' data */ + if (TI_BANDGAP_HAS(bgp, TALERT)) + free_irq(bgp->irq, bgp); + /* Remove sensor interfaces */ for (i = 0; i < bgp->conf->sensor_count; i++) { if (bgp->conf->sensors[i].unregister_cooling) @@ -1093,9 +1097,6 @@ void ti_bandgap_remove(struct platform_device *pdev) clk_put(bgp->fclock); clk_put(bgp->div_clk); - if (TI_BANDGAP_HAS(bgp, TALERT)) - free_irq(bgp->irq, bgp); - if (TI_BANDGAP_HAS(bgp, TSHUT)) free_irq(gpiod_to_irq(bgp->tshut_gpiod), NULL); } diff --git a/drivers/thermal/ti-soc-thermal/ti-thermal-common.c b/drivers/thermal/ti-soc-thermal/ti-thermal-common.c index 0cf0826b805a..4bf3b2e0fc68 100644 --- a/drivers/thermal/ti-soc-thermal/ti-thermal-common.c +++ b/drivers/thermal/ti-soc-thermal/ti-thermal-common.c @@ -195,6 +195,10 @@ int ti_thermal_remove_sensor(struct ti_bandgap *bgp, int id) data = ti_bandgap_get_sensor_data(bgp, id); + /* Work queued by the talert IRQ must not outlive the data */ + if (!IS_ERR_OR_NULL(data)) + cancel_work_sync(&data->thermal_wq); + if (!IS_ERR_OR_NULL(data) && data->ti_thermal) { if (data->our_zone) thermal_zone_device_unregister(data->ti_thermal);