From 0ee5c5d804d592a8328a425b9274487d393d3a05 Mon Sep 17 00:00:00 2001 From: Liu Dalin Date: Thu, 27 Aug 2026 09:54:44 +0800 Subject: [PATCH 1/6] rtc: dev: zero-initialize struct rtc_wkalrm to prevent information leak The struct rtc_wkalrm has padding holes after the 'pending' member. When returned to userspace via the RTC_WKALRM_RD ioctl, uninitialized kernel stack data residing in these holes may be leaked. Zero-initialize the struct at declaration to eliminate this information leak risk. Fixes smatch warnings: - drivers/rtc/dev.c:392 rtc_dev_ioctl() warn: check that 'alarm' doesn't leak information (struct has a hole after 'pending') Fixes: 36e14f5fdfdf ("rtc: rename core files") Assisted-by: smatch:2.0 [static analysis] Signed-off-by: Liu Dalin Link: https://patch.msgid.link/B28B7DE18F3A9447+20260827015444.3314227-1-liudalin@kylinsec.com.cn Signed-off-by: Alexandre Belloni --- drivers/rtc/dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/rtc/dev.c b/drivers/rtc/dev.c index 8ba7c25d2565..be173c9511fa 100644 --- a/drivers/rtc/dev.c +++ b/drivers/rtc/dev.c @@ -216,7 +216,7 @@ static long rtc_dev_ioctl(struct file *file, struct rtc_device *rtc = file->private_data; const struct rtc_class_ops *ops = rtc->ops; struct rtc_time tm; - struct rtc_wkalrm alarm; + struct rtc_wkalrm alarm = { 0 }; struct rtc_param param; void __user *uarg = (void __user *)arg; From b430d1f6d80c005b486f028910e1ac3c91812710 Mon Sep 17 00:00:00 2001 From: "Johnny.Fan" Date: Mon, 31 Aug 2026 22:00:14 +0800 Subject: [PATCH 2/6] rtc: efi: restore alarm support with runtime capability probe Restore the EFI RTC alarm functionality (read_alarm/set_alarm and procfs output) that was removed by commit 18a3510bc87d ("rtc: efi: Remove wakeup functionality"). The removal was motivated by the observation that many EFI platforms lack RTC wakeup support while also omitting the RT_PROP table, causing the wakeup capability to be incorrectly advertised. While that is true, removing the feature entirely also breaks platforms where the wakeup runtime service actually works. On CIX SoCs, the RTC is an I2C device owned by firmware, and in the ACPI boot configuration the I2C bus it sits on is not exposed to the OS, so the EFI runtime services are the only way Linux can access the RTC at all. Both Linux and Windows use this same interface by design, so the RTC state is consistent across the operating systems installed on the machine, and Windows relies on the EFI wakeup services for its own RTC alarm support. With the alarm support removed from rtc-efi, Linux loses the equivalent capability entirely on these platforms: rtcwake fails for both suspend-to-RAM and power-off wakeup, as there is no other path to program the RTC alarm. This is a real product requirement that CIX customers have explicitly requested, not a hypothetical use case. To avoid exposing a broken alarm on firmware that does not implement the wakeup runtime services, probe the capability at ->probe() time by actually calling GetWakeupTime() -- if it fails, clear RTC_FEATURE_ALARM and the device behaves as before the removal. Also set RTC_FEATURE_ALARM_WAKEUP_ONLY since EFI wakeup time is a wakeup-only alarm, not a periodic/update interrupt source. Acked-by: Ard Biesheuvel Reviewed-by: Feng Tang Reviewed-by: Fugang Duan Signed-off-by: Johnny.Fan Link: https://patch.msgid.link/20260831140014.839222-1-johnny.fan@cixtech.com Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-efi.c | 80 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 78 insertions(+), 2 deletions(-) diff --git a/drivers/rtc/rtc-efi.c b/drivers/rtc/rtc-efi.c index b4f44999ef0f..a8c18ebc5599 100644 --- a/drivers/rtc/rtc-efi.c +++ b/drivers/rtc/rtc-efi.c @@ -52,6 +52,7 @@ compute_wday(efi_time_t *eft, int yday) static void convert_to_efi_time(struct rtc_time *wtime, efi_time_t *eft) { + memset(eft, 0, sizeof(*eft)); eft->year = wtime->tm_year + 1900; eft->month = wtime->tm_mon + 1; eft->day = wtime->tm_mday; @@ -112,6 +113,35 @@ convert_from_efi_time(efi_time_t *eft, struct rtc_time *wtime) return true; } +static int efi_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) +{ + efi_time_t eft; + efi_status_t status; + + memset(&eft, 0, sizeof(eft)); + status = efi.get_wakeup_time((efi_bool_t *)&wkalrm->enabled, + (efi_bool_t *)&wkalrm->pending, &eft); + if (status != EFI_SUCCESS) + return -EINVAL; + + if (!convert_from_efi_time(&eft, &wkalrm->time)) + return -EIO; + + return rtc_valid_tm(&wkalrm->time); +} + +static int efi_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) +{ + efi_time_t eft; + efi_status_t status; + + convert_to_efi_time(&wkalrm->time, &eft); + + status = efi.set_wakeup_time((efi_bool_t)!!wkalrm->enabled, &eft); + + return status == EFI_SUCCESS ? 0 : -EINVAL; +} + static int efi_read_time(struct device *dev, struct rtc_time *tm) { efi_status_t status; @@ -146,13 +176,21 @@ static int efi_set_time(struct device *dev, struct rtc_time *tm) static int efi_procfs(struct device *dev, struct seq_file *seq) { - efi_time_t eft; + efi_time_t eft, alm; efi_time_cap_t cap; + efi_bool_t enabled, pending; + struct rtc_device *rtc = dev_get_drvdata(dev); memset(&eft, 0, sizeof(eft)); + memset(&alm, 0, sizeof(alm)); memset(&cap, 0, sizeof(cap)); efi.get_time(&eft, &cap); + if (test_bit(RTC_FEATURE_ALARM, rtc->features) && + efi.get_wakeup_time(&enabled, &pending, &alm) != EFI_SUCCESS) { + enabled = 0; + pending = 0; + } seq_printf(seq, "Time\t\t: %u:%u:%u.%09u\n" @@ -168,6 +206,25 @@ static int efi_procfs(struct device *dev, struct seq_file *seq) /* XXX fixme: convert to string? */ seq_printf(seq, "Timezone\t: %u\n", eft.timezone); + if (test_bit(RTC_FEATURE_ALARM, rtc->features)) { + seq_printf(seq, + "Alarm Time\t: %u:%u:%u.%09u\n" + "Alarm Date\t: %u-%u-%u\n" + "Alarm Daylight\t: %u\n" + "Enabled\t\t: %s\n" + "Pending\t\t: %s\n", + alm.hour, alm.minute, alm.second, alm.nanosecond, + alm.year, alm.month, alm.day, + alm.daylight, + enabled == 1 ? "yes" : "no", + pending == 1 ? "yes" : "no"); + + if (alm.timezone == EFI_UNSPECIFIED_TIMEZONE) + seq_puts(seq, "Alarm Timezone\t: unspecified\n"); + else + seq_printf(seq, "Alarm Timezone\t: %d\n", alm.timezone); + } + /* * now prints the capabilities */ @@ -183,6 +240,8 @@ static int efi_procfs(struct device *dev, struct seq_file *seq) static const struct rtc_class_ops efi_rtc_ops = { .read_time = efi_read_time, .set_time = efi_set_time, + .read_alarm = efi_read_alarm, + .set_alarm = efi_set_alarm, .proc = efi_procfs, }; @@ -191,6 +250,7 @@ static int __init efi_rtc_probe(struct platform_device *dev) struct rtc_device *rtc; efi_time_t eft; efi_time_cap_t cap; + efi_bool_t enabled, pending; /* First check if the RTC is usable */ if (efi.get_time(&eft, &cap) != EFI_SUCCESS) @@ -203,7 +263,23 @@ static int __init efi_rtc_probe(struct platform_device *dev) platform_set_drvdata(dev, rtc); rtc->ops = &efi_rtc_ops; - clear_bit(RTC_FEATURE_ALARM, rtc->features); + clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->features); + + /* + * The EFI_RT_SUPPORTED_WAKEUP_SERVICES bit defaults to enabled + * and only gets cleared when the RT_PROP table explicitly says + * wakeup is unsupported. Many platforms lack an RT_PROP table + * even though they don't implement the wakeup runtime service, + * so probe by actually calling GetWakeupTime() to avoid exposing + * a broken alarm to userspace. + */ + if (efi_rt_services_supported(EFI_RT_SUPPORTED_WAKEUP_SERVICES) && + efi.get_wakeup_time(&enabled, &pending, &eft) == EFI_SUCCESS) { + set_bit(RTC_FEATURE_ALARM, rtc->features); + set_bit(RTC_FEATURE_ALARM_WAKEUP_ONLY, rtc->features); + } else { + clear_bit(RTC_FEATURE_ALARM, rtc->features); + } device_init_wakeup(&dev->dev, true); From 6a4117eee82dd85f11bf898bf66026764011eeb6 Mon Sep 17 00:00:00 2001 From: Aamir Ahmed Date: Sat, 5 Sep 2026 19:38:08 +0100 Subject: [PATCH 3/6] rtc: ac100: Assign .num before accessing .hws Commit f316cdff8d67 ("clk: Annotate struct clk_hw_onecell_data with __counted_by") annotated the hws member of 'struct clk_hw_onecell_data' with __counted_by, which informs the bounds sanitizer (UBSAN_BOUNDS) about the number of elements in .hws[], so that it can warn when .hws[] is accessed out of bounds. As noted in that change, the __counted_by member must be initialized with the number of elements before the first array access happens, otherwise there will be a warning from each access prior to the initialization because the number of elements is zero. This occurs in ac100_rtc_register_clks() due to .num being assigned only after every clkout clock has been stored in .hws[]. With CONFIG_UBSAN_BOUNDS and a compiler that implements __counted_by (GCC 15.1+ or Clang 20.1+), this triggers an array-index-out-of-bounds report during probe, and with CONFIG_UBSAN_TRAP the first store traps. Initialize .num with AC100_CLKOUT_NUM, the number of elements .hws[] was allocated with, right after the allocation. That is the value the loop counter ends up at on the success path anyway, so the provider's behaviour is unchanged. Cc: stable@vger.kernel.org Fixes: f316cdff8d67 ("clk: Annotate struct clk_hw_onecell_data with __counted_by") Assisted-by: LLM Signed-off-by: Aamir Ahmed Reviewed-by: Chen-Yu Tsai Reviewed-by: Gustavo A. R. Silva Link: https://patch.msgid.link/AS8P251MB00013E724A77A355668B6CCEC8B42@AS8P251MB0001.EURP251.PROD.OUTLOOK.COM Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-ac100.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-ac100.c b/drivers/rtc/rtc-ac100.c index bba7115ff3ad..a2f465438fd0 100644 --- a/drivers/rtc/rtc-ac100.c +++ b/drivers/rtc/rtc-ac100.c @@ -317,6 +317,8 @@ static int ac100_rtc_register_clks(struct ac100_rtc_dev *chip) if (!chip->clk_data) return -ENOMEM; + chip->clk_data->num = AC100_CLKOUT_NUM; + chip->rtc_32k_clk = clk_hw_register_fixed_rate(chip->dev, AC100_RTC_32K_NAME, NULL, 0, @@ -360,7 +362,6 @@ static int ac100_rtc_register_clks(struct ac100_rtc_dev *chip) chip->clk_data->hws[i] = &clk->hw; } - chip->clk_data->num = i; ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, chip->clk_data); if (ret) goto err_unregister_rtc_32k; From fcf0b58e976e83adf246b014518e49c662b96f5e Mon Sep 17 00:00:00 2001 From: Aamir Ahmed Date: Sat, 5 Sep 2026 21:41:52 +0100 Subject: [PATCH 4/6] rtc: ac100: Fix clock provider use-after-free on probe failure ac100_rtc_register_clks() registers the RTC-32k clock with clk_hw_register_fixed_rate() and the clock provider with of_clk_add_hw_provider(). Neither is device managed, and both are released only from ac100_rtc_remove(). The driver core does not call remove() when probe() fails: really_probe() reaches probe_failed below the device_remove() call and goes straight to releasing the device's managed resources. ac100_rtc_probe() ends with return devm_rtc_register_device(chip->rtc); so if that fails after the clocks have been registered, the provider is left on the global of_clk_providers list holding chip->clk_data, which was allocated with devm_kzalloc() and is freed while probe() unwinds. A later lookup on this device tree node then reads freed memory in of_clk_hw_onecell_get(). Consumers that do exactly that exist in tree: the wifi power sequence nodes on sun8i-a83t-bananapi-m3 and sun8i-a83t-cubietruck-plus take <&ac100_rtc 1>, and the sun9i-a80 boards route osc32k through <&ac100_rtc 0>. The window is narrow, as devm_rtc_register_device() can only fail with -ENOMEM here, but the provider is left dangling whenever it does. The RTC-32k clock is leaked on the same path. Since clk_core_lookup() searches a global list that is not scoped per device, __clk_register() rejects the duplicate name with -EEXIST, so the leak also makes a later probe of the same device fail. The error path that returns -EINVAL when the ADDA 4M parent clock cannot be found leaks the RTC-32k clock in the same way. No provider has been registered at that point, so that one is a leak rather than a use-after-free. Register both with devm_clk_hw_register_fixed_rate() and devm_of_clk_add_hw_provider() so that they are released whenever the device goes away, on a failed probe as well as on unbind. Devres releases in reverse order of acquisition, so the provider is removed before chip->clk_data is freed, and the clkout children are now unregistered before their parent rather than after it. ac100_rtc_unregister_clks() and the remove callback then have nothing left to do and are removed. Fixes: d00a18a42c14 ("rtc: ac100: Add RTC driver for X-Powers AC100") Reported-by: Sashiko AI Closes: https://lore.kernel.org/linux-rtc/20260905184936.155E11F00A3A@smtp.kernel.org/ Assisted-by: LLM Signed-off-by: Aamir Ahmed Link: https://patch.msgid.link/AS8P251MB0001A489CC45A591ABBB9BDCC8B42@AS8P251MB0001.EURP251.PROD.OUTLOOK.COM Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-ac100.c | 36 +++++++----------------------------- 1 file changed, 7 insertions(+), 29 deletions(-) diff --git a/drivers/rtc/rtc-ac100.c b/drivers/rtc/rtc-ac100.c index a2f465438fd0..c579c4bf5578 100644 --- a/drivers/rtc/rtc-ac100.c +++ b/drivers/rtc/rtc-ac100.c @@ -319,10 +319,10 @@ static int ac100_rtc_register_clks(struct ac100_rtc_dev *chip) chip->clk_data->num = AC100_CLKOUT_NUM; - chip->rtc_32k_clk = clk_hw_register_fixed_rate(chip->dev, - AC100_RTC_32K_NAME, - NULL, 0, - AC100_RTC_32K_RATE); + chip->rtc_32k_clk = devm_clk_hw_register_fixed_rate(chip->dev, + AC100_RTC_32K_NAME, + NULL, 0, + AC100_RTC_32K_RATE); if (IS_ERR(chip->rtc_32k_clk)) { ret = PTR_ERR(chip->rtc_32k_clk); dev_err(chip->dev, "Failed to register RTC-32k clock: %d\n", @@ -356,28 +356,14 @@ static int ac100_rtc_register_clks(struct ac100_rtc_dev *chip) if (ret) { dev_err(chip->dev, "Failed to register clk '%s': %d\n", init.name, ret); - goto err_unregister_rtc_32k; + return ret; } chip->clk_data->hws[i] = &clk->hw; } - ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, chip->clk_data); - if (ret) - goto err_unregister_rtc_32k; - - return 0; - -err_unregister_rtc_32k: - clk_unregister_fixed_rate(chip->rtc_32k_clk->clk); - - return ret; -} - -static void ac100_rtc_unregister_clks(struct ac100_rtc_dev *chip) -{ - of_clk_del_provider(chip->dev->of_node); - clk_unregister_fixed_rate(chip->rtc_32k_clk->clk); + return devm_of_clk_add_hw_provider(chip->dev, of_clk_hw_onecell_get, + chip->clk_data); } /* @@ -615,13 +601,6 @@ static int ac100_rtc_probe(struct platform_device *pdev) return devm_rtc_register_device(chip->rtc); } -static void ac100_rtc_remove(struct platform_device *pdev) -{ - struct ac100_rtc_dev *chip = platform_get_drvdata(pdev); - - ac100_rtc_unregister_clks(chip); -} - static const struct of_device_id ac100_rtc_match[] = { { .compatible = "x-powers,ac100-rtc" }, { }, @@ -630,7 +609,6 @@ MODULE_DEVICE_TABLE(of, ac100_rtc_match); static struct platform_driver ac100_rtc_driver = { .probe = ac100_rtc_probe, - .remove = ac100_rtc_remove, .driver = { .name = "ac100-rtc", .of_match_table = of_match_ptr(ac100_rtc_match), From ac41b05d15db3134e839148290d67415ee0bdb58 Mon Sep 17 00:00:00 2001 From: Manush Prajwal Date: Sun, 6 Sep 2026 16:44:09 +0530 Subject: [PATCH 5/6] rtc: mpfs: fix unchecked devm_clk_get() error pointer in probe() devm_clk_get(&pdev->dev, "rtcref")'s return value was passed straight into clk_get_rate() without checking it for an error first, unlike the "rtc" clock a few lines above which is correctly checked with IS_ERR(). clk_get_rate() only guards against a NULL clk, not an error pointer: if (!clk) return 0; ... rate = clk_core_get_rate_recalc(clk->core); so if devm_clk_get() ever returns an error pointer here (for example ERR_PTR(-EPROBE_DEFER), which is the normal, expected outcome if the clkcfg clock-provider this RTC depends on has not registered its clocks yet by the time this driver probes), clk_get_rate() dereferences that error pointer instead of returning 0, crashing instead of letting probe defer. Capture the clock in the existing 'clk' local and check it with IS_ERR() before calling clk_get_rate(), matching the handling already used for the "rtc" clock in this same function. Signed-off-by: Manush Prajwal Fixes: 0b31d703598d ("rtc: Add driver for Microchip PolarFire SoC") Reviewed-by: Conor Dooley Link: https://patch.msgid.link/6a9d4b02.7d74b517.206c4c.75f8@mx.google.com Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-mpfs.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-mpfs.c b/drivers/rtc/rtc-mpfs.c index ece6de4a6adb..60596b0ea9d8 100644 --- a/drivers/rtc/rtc-mpfs.c +++ b/drivers/rtc/rtc-mpfs.c @@ -256,8 +256,12 @@ static int mpfs_rtc_probe(struct platform_device *pdev) return ret; } + clk = devm_clk_get(&pdev->dev, "rtcref"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + /* prescaler hardware adds 1 to reg value */ - prescaler = clk_get_rate(devm_clk_get(&pdev->dev, "rtcref")) - 1; + prescaler = clk_get_rate(clk) - 1; if (prescaler > MAX_PRESCALER_COUNT) { dev_dbg(&pdev->dev, "invalid prescaler %lu\n", prescaler); return -EINVAL; From 055ef5ce9f67a6a3a1363fa663007f8196cc0fb8 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Wed, 2 Sep 2026 16:03:18 +0800 Subject: [PATCH 6/6] rtc: spear: initialize IRQ state before requesting alarm IRQ devm_request_irq() enables the interrupt before it returns, so the handler may run while probe is still initializing the device. Initialize the MMIO address and spinlock before requesting the alarm IRQ. Fixes: 0942a71e435f ("rtc: add support for spear rtc") Cc: stable@vger.kernel.org Assisted-by: Codex:GPT-5 Signed-off-by: Runyu Xiao Link: https://patch.msgid.link/20260902080318.3498434-1-runyu.xiao@seu.edu.cn Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-spear.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/rtc/rtc-spear.c b/drivers/rtc/rtc-spear.c index 959acff8faff..d12bccb3cf5e 100644 --- a/drivers/rtc/rtc-spear.c +++ b/drivers/rtc/rtc-spear.c @@ -363,6 +363,12 @@ static int spear_rtc_probe(struct platform_device *pdev) if (irq < 0) return irq; + config->ioaddr = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(config->ioaddr)) + return PTR_ERR(config->ioaddr); + + spin_lock_init(&config->lock); + status = devm_request_irq(&pdev->dev, irq, spear_rtc_irq, 0, pdev->name, config); if (status) { @@ -371,10 +377,6 @@ static int spear_rtc_probe(struct platform_device *pdev) return status; } - config->ioaddr = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(config->ioaddr)) - return PTR_ERR(config->ioaddr); - config->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(config->clk)) return PTR_ERR(config->clk); @@ -383,7 +385,6 @@ static int spear_rtc_probe(struct platform_device *pdev) if (status < 0) return status; - spin_lock_init(&config->lock); platform_set_drvdata(pdev, config); config->rtc->ops = &spear_rtc_ops;