summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexandre Belloni <alexandre.belloni@bootlin.com>2019-10-21 01:13:20 +0200
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2019-12-17 20:08:35 +0100
commitec968952480d2c4168440b6a0b3aada66afe273a (patch)
tree8d77d5b3ce260dc84e3346471f125f532f9136d7
parent3b353b2b9d8a092982866422020b1c47e285cd83 (diff)
downloadlinux-stable-ec968952480d2c4168440b6a0b3aada66afe273a.tar.gz
linux-stable-ec968952480d2c4168440b6a0b3aada66afe273a.tar.bz2
linux-stable-ec968952480d2c4168440b6a0b3aada66afe273a.zip
rtc: disable uie before setting time and enable after
commit 7e7c005b4b1f1f169bcc4b2c3a40085ecc663df2 upstream. When setting the time in the future with the uie timer enabled, rtc_timer_do_work will loop for a while because the expiration of the uie timer was way before the current RTC time and a new timer will be enqueued until the current rtc time is reached. If the uie timer is enabled, disable it before setting the time and enable it after expiring current timers (which may actually be an alarm). This is the safest thing to do to ensure the uie timer is still synchronized with the RTC, especially in the UIE emulation case. Reported-by: syzbot+08116743f8ad6f9a6de7@syzkaller.appspotmail.com Fixes: 6610e0893b8b ("RTC: Rework RTC code to use timerqueue for events") Link: https://lore.kernel.org/r/20191020231320.8191-1-alexandre.belloni@bootlin.com Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/rtc/interface.c19
1 files changed, 18 insertions, 1 deletions
diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c
index 72b7ddc43116..60a0341d7f58 100644
--- a/drivers/rtc/interface.c
+++ b/drivers/rtc/interface.c
@@ -125,7 +125,7 @@ EXPORT_SYMBOL_GPL(rtc_read_time);
int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm)
{
- int err;
+ int err, uie;
err = rtc_valid_tm(tm);
if (err != 0)
@@ -137,6 +137,17 @@ int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm)
rtc_subtract_offset(rtc, tm);
+#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL
+ uie = rtc->uie_rtctimer.enabled || rtc->uie_irq_active;
+#else
+ uie = rtc->uie_rtctimer.enabled;
+#endif
+ if (uie) {
+ err = rtc_update_irq_enable(rtc, 0);
+ if (err)
+ return err;
+ }
+
err = mutex_lock_interruptible(&rtc->ops_lock);
if (err)
return err;
@@ -153,6 +164,12 @@ int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm)
/* A timer might have just expired */
schedule_work(&rtc->irqwork);
+ if (uie) {
+ err = rtc_update_irq_enable(rtc, 1);
+ if (err)
+ return err;
+ }
+
trace_rtc_set_time(rtc_tm_to_time64(tm), err);
return err;
}