From: hyunjungg <hj351016@gmail.com>
To: CL Wang <cl634@andestech.com>,
Alexandre Belloni <alexandre.belloni@bootlin.com>,
linux-rtc@vger.kernel.org
Cc: linux-kernel@vger.kernel.org, Hyunjung Ko <hj351016@gmail.com>,
stable@vger.kernel.org
Subject: [PATCH] rtc: atcrtc100: drain the alarm work before the RTC device is freed
Date: Sat, 7 Nov 2026 15:37:11 +0900 [thread overview]
Message-ID: <20261107063711.3388440-1-hj351016@gmail.com> (raw)
From: Hyunjung Ko <hj351016@gmail.com>
atcrtc_alarm_isr() schedules atcrtc_dev->rtc_work (atcrtc_alarm_clear),
whose handler obtains the atcrtc_dev via container_of() and dereferences
rtc->rtc_dev (guard(rtc_ops)(rtc->rtc_dev)).
atcrtc_probe() requested the alarm IRQ with devm_request_irq() *before*
allocating rtc_dev with devm_rtc_allocate_device(), and INIT_WORK()'d
rtc_work last with no teardown cancel. Because devres unwinds in reverse
registration order, on unbind the RTC device is freed before the IRQ, and
rtc_work is never cancelled at all: an alarm interrupt arriving around
unbind can run atcrtc_alarm_clear() after rtc->rtc_dev (and the devm
driver data) have been freed -> use-after-free.
Allocate the RTC device first, then register rtc_work for device-managed
cancellation with devm_work_autocancel(), and only then request the IRQ.
devres now frees the IRQ first (so it can no longer schedule the work),
then drains the work while rtc_dev is still valid, then frees rtc_dev and
the driver data.
Found by code review; not reproduced (requires ATCRTC100 hardware and an
alarm interrupt racing device unbind).
Fixes: 7adca706fe16 ("rtc: atcrtc100: Add ATCRTC100 RTC driver")
Cc: stable@vger.kernel.org
Signed-off-by: Hyunjung Ko <hj351016@gmail.com>
---
drivers/rtc/rtc-atcrtc100.c | 30 +++++++++++++++++++++---------
1 file changed, 21 insertions(+), 9 deletions(-)
diff --git a/drivers/rtc/rtc-atcrtc100.c b/drivers/rtc/rtc-atcrtc100.c
index 2433df3..51a38f0 100644
--- a/drivers/rtc/rtc-atcrtc100.c
+++ b/drivers/rtc/rtc-atcrtc100.c
@@ -7,6 +7,7 @@
#include <linux/bitfield.h>
#include <linux/delay.h>
+#include <linux/devm-helpers.h>
#include <linux/interrupt.h>
#include <linux/math64.h>
#include <linux/module.h>
@@ -301,6 +302,26 @@ static int atcrtc_probe(struct platform_device *pdev)
"Failed to get IRQ for alarm\n");
atcrtc_dev->alarm_irq = ret;
+ atcrtc_dev->rtc_dev = devm_rtc_allocate_device(&pdev->dev);
+ if (IS_ERR(atcrtc_dev->rtc_dev))
+ return dev_err_probe(&pdev->dev, PTR_ERR(atcrtc_dev->rtc_dev),
+ "Failed to allocate RTC device\n");
+
+ set_bit(RTC_FEATURE_ALARM, atcrtc_dev->rtc_dev->features);
+ atcrtc_dev->rtc_dev->ops = &rtc_ops;
+
+ /*
+ * Register rtc_work for device-managed cancellation after the RTC
+ * device it dereferences and before the IRQ that schedules it, so on
+ * unbind devres frees the IRQ, then drains the work, then frees the
+ * RTC device and driver data -- never the reverse (atcrtc_alarm_clear()
+ * touches rtc->rtc_dev).
+ */
+ ret = devm_work_autocancel(&pdev->dev, &atcrtc_dev->rtc_work,
+ atcrtc_alarm_clear);
+ if (ret)
+ return ret;
+
ret = devm_request_irq(&pdev->dev,
atcrtc_dev->alarm_irq,
atcrtc_alarm_isr,
@@ -312,12 +333,6 @@ static int atcrtc_probe(struct platform_device *pdev)
"Failed to request IRQ %d for alarm\n",
atcrtc_dev->alarm_irq);
- atcrtc_dev->rtc_dev = devm_rtc_allocate_device(&pdev->dev);
- if (IS_ERR(atcrtc_dev->rtc_dev))
- return dev_err_probe(&pdev->dev, PTR_ERR(atcrtc_dev->rtc_dev),
- "Failed to allocate RTC device\n");
-
- set_bit(RTC_FEATURE_ALARM, atcrtc_dev->rtc_dev->features);
ret = device_init_wakeup(&pdev->dev, true);
if (ret)
return dev_err_probe(&pdev->dev, ret,
@@ -330,9 +345,6 @@ static int atcrtc_probe(struct platform_device *pdev)
"Failed to set wake IRQ\n");
}
- atcrtc_dev->rtc_dev->ops = &rtc_ops;
-
- INIT_WORK(&atcrtc_dev->rtc_work, atcrtc_alarm_clear);
return devm_rtc_register_device(atcrtc_dev->rtc_dev);
}
--
2.43.0
reply other threads:[~2026-11-07 6:37 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261107063711.3388440-1-hj351016@gmail.com \
--to=hj351016@gmail.com \
--cc=alexandre.belloni@bootlin.com \
--cc=cl634@andestech.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-rtc@vger.kernel.org \
--cc=stable@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®