[PATCH] rtc: atcrtc100: cancel alarm work on remove
From: Fan Wu
Date: Wed Sep 09 2026 - 12:41:00 EST
The alarm interrupt handler atcrtc_alarm_isr() queues rtc_work on the
system workqueue to clear the alarm. The handler atcrtc_alarm_clear()
dereferences the devm-managed atcrtc_dev through container_of(), takes
rtc_lock() on the RTC device and writes the regmap.
The driver has no remove callback, and the devres cleanup only frees
the interrupt before the remaining resources. free_irq() waits for the
interrupt handler, but it does not cancel work the handler already
queued. A pending atcrtc_alarm_clear() can therefore run after the
regmap, the I/O mapping and finally the device structure have been
released, and dereference freed memory.
Add a remove callback that frees the interrupt first, so no new work
can be queued, and then cancels the alarm work, following the same
pattern as rtc-ds1374 and rtc-ds1305.
It also clears the wake IRQ and disables the wakeup source configured
in probe. These are not devres-managed, and the driver core only
releases them when the device itself is removed, not on unbind.
Without this cleanup, a later bind would fail probe with -EEXIST:
device_wakeup_attach() rejects a second wakeup source for the same
device, and the leftover wake IRQ would make dev_pm_set_wake_irq()
fail with -EEXIST and a WARN as well.
This issue was found by an in-house static analysis tool.
Fixes: 7adca706fe16 ("rtc: atcrtc100: Add ATCRTC100 RTC driver")
Cc: stable@xxxxxxxxxxxxxxx
Assisted-by: Codex:gpt-5.6
Co-developed-by: Song Li <songl@xxxxxxxxxx>
Signed-off-by: Song Li <songl@xxxxxxxxxx>
Signed-off-by: Fan Wu <fanwu01@xxxxxxxxxx>
---
drivers/rtc/rtc-atcrtc100.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/drivers/rtc/rtc-atcrtc100.c b/drivers/rtc/rtc-atcrtc100.c
index 9808fc2c5a49..3c7313218da5 100644
--- a/drivers/rtc/rtc-atcrtc100.c
+++ b/drivers/rtc/rtc-atcrtc100.c
@@ -337,6 +337,16 @@ static int atcrtc_probe(struct platform_device *pdev)
return devm_rtc_register_device(atcrtc_dev->rtc_dev);
}
+static void atcrtc_remove(struct platform_device *pdev)
+{
+ struct atcrtc_dev *atcrtc_dev = platform_get_drvdata(pdev);
+
+ dev_pm_clear_wake_irq(&pdev->dev);
+ device_init_wakeup(&pdev->dev, false);
+ devm_free_irq(&pdev->dev, atcrtc_dev->alarm_irq, atcrtc_dev);
+ cancel_work_sync(&atcrtc_dev->rtc_work);
+}
+
static int atcrtc_resume(struct device *dev)
{
struct atcrtc_dev *rtc = dev_get_drvdata(dev);
@@ -372,6 +382,7 @@ static struct platform_driver atcrtc_platform_driver = {
.pm = pm_sleep_ptr(&atcrtc_pm_ops),
},
.probe = atcrtc_probe,
+ .remove = atcrtc_remove,
};
module_platform_driver(atcrtc_platform_driver);