From e873c74132f0c5f1452816cd9bb26208f0bba1e1 Mon Sep 17 00:00:00 2001 From: Shivank Garg Date: Sat, 22 Aug 2026 19:22:05 +0000 Subject: dmaengine: fix use-after-free in dma_chan_put() and dma_release_channel() When dma_device_put() drops the last reference on chan->device->ref, dma_device_release() runs and may free the dma_device along with its channels. dma_chan_put() then still reads chan->device->owner via dma_chan_to_owner() for the trailing module_put(). KASAN catches it: slab-use-after-free in dma_chan_put+0x3e6/0x4c0 Read of size 8 by task insmod/6319 Freed by task 6319: kfree+0x225/0x470 dma_chan_put+0x395/0x4c0 dmaengine_put+0xf8/0x160 Cache the module owner in dma_chan_put() before the put so the trailing module_put() does not need chan->device. Fixes: 8ad342a86359 ("dmaengine: Add reference counting to dma_device struct") Suggested-by: Sashiko Link: https://sashiko.dev/#/patchset/20260518-dmaengine-kref-fix-v1-1-4d6125048fb7@amd.com Reviewed-by: Frank Li Reviewed-by: Logan Gunthorpe Signed-off-by: Shivank Garg Link: https://patch.msgid.link/20260822-dmaengine-kref-fix-v5-3-d4a4ee47d927@amd.com Signed-off-by: Vinod Koul --- drivers/rtc/rtc-test.c | 201 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 drivers/rtc/rtc-test.c (limited to 'drivers/rtc/rtc-test.c') diff --git a/drivers/rtc/rtc-test.c b/drivers/rtc/rtc-test.c new file mode 100644 index 000000000..94f995feb --- /dev/null +++ b/drivers/rtc/rtc-test.c @@ -0,0 +1,201 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * An RTC test device/driver + * Copyright (C) 2005 Tower Technologies + * Author: Alessandro Zummo + */ + +#include +#include +#include +#include + +#define MAX_RTC_TEST 3 + +struct rtc_test_data { + struct rtc_device *rtc; + time64_t offset; + struct timer_list alarm; + bool alarm_en; +}; + +static struct platform_device *pdev[MAX_RTC_TEST]; + +static int test_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct rtc_test_data *rtd = dev_get_drvdata(dev); + time64_t alarm; + + alarm = (rtd->alarm.expires - jiffies) / HZ; + alarm += ktime_get_real_seconds() + rtd->offset; + + rtc_time64_to_tm(alarm, &alrm->time); + alrm->enabled = rtd->alarm_en; + + return 0; +} + +static int test_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct rtc_test_data *rtd = dev_get_drvdata(dev); + ktime_t timeout; + u64 expires; + + timeout = rtc_tm_to_time64(&alrm->time) - ktime_get_real_seconds(); + timeout -= rtd->offset; + + timer_delete(&rtd->alarm); + + expires = jiffies + timeout * HZ; + if (expires > U32_MAX) + expires = U32_MAX; + + rtd->alarm.expires = expires; + + if (alrm->enabled) + add_timer(&rtd->alarm); + + rtd->alarm_en = alrm->enabled; + + return 0; +} + +static int test_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct rtc_test_data *rtd = dev_get_drvdata(dev); + + rtc_time64_to_tm(ktime_get_real_seconds() + rtd->offset, tm); + + return 0; +} + +static int test_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct rtc_test_data *rtd = dev_get_drvdata(dev); + + rtd->offset = rtc_tm_to_time64(tm) - ktime_get_real_seconds(); + + return 0; +} + +static int test_rtc_alarm_irq_enable(struct device *dev, unsigned int enable) +{ + struct rtc_test_data *rtd = dev_get_drvdata(dev); + + rtd->alarm_en = enable; + if (enable) + add_timer(&rtd->alarm); + else + timer_delete(&rtd->alarm); + + return 0; +} + +static const struct rtc_class_ops test_rtc_ops_noalm = { + .read_time = test_rtc_read_time, + .set_time = test_rtc_set_time, + .alarm_irq_enable = test_rtc_alarm_irq_enable, +}; + +static const struct rtc_class_ops test_rtc_ops = { + .read_time = test_rtc_read_time, + .set_time = test_rtc_set_time, + .read_alarm = test_rtc_read_alarm, + .set_alarm = test_rtc_set_alarm, + .alarm_irq_enable = test_rtc_alarm_irq_enable, +}; + +static void test_rtc_alarm_handler(struct timer_list *t) +{ + struct rtc_test_data *rtd = timer_container_of(rtd, t, alarm); + + rtc_update_irq(rtd->rtc, 1, RTC_AF | RTC_IRQF); +} + +static int test_probe(struct platform_device *plat_dev) +{ + struct rtc_test_data *rtd; + + rtd = devm_kzalloc(&plat_dev->dev, sizeof(*rtd), GFP_KERNEL); + if (!rtd) + return -ENOMEM; + + platform_set_drvdata(plat_dev, rtd); + + rtd->rtc = devm_rtc_allocate_device(&plat_dev->dev); + if (IS_ERR(rtd->rtc)) + return PTR_ERR(rtd->rtc); + + switch (plat_dev->id) { + case 0: + rtd->rtc->ops = &test_rtc_ops_noalm; + break; + default: + rtd->rtc->ops = &test_rtc_ops; + device_init_wakeup(&plat_dev->dev, true); + } + + timer_setup(&rtd->alarm, test_rtc_alarm_handler, 0); + rtd->alarm.expires = 0; + + return devm_rtc_register_device(rtd->rtc); +} + +static struct platform_driver test_driver = { + .probe = test_probe, + .driver = { + .name = "rtc-test", + }, +}; + +static int __init test_init(void) +{ + int i, err; + + err = platform_driver_register(&test_driver); + if (err) + return err; + + err = -ENOMEM; + for (i = 0; i < MAX_RTC_TEST; i++) { + pdev[i] = platform_device_alloc("rtc-test", i); + if (!pdev[i]) + goto exit_free_mem; + } + + for (i = 0; i < MAX_RTC_TEST; i++) { + err = platform_device_add(pdev[i]); + if (err) + goto exit_device_del; + } + + return 0; + +exit_device_del: + for (; i > 0; i--) + platform_device_del(pdev[i - 1]); + +exit_free_mem: + for (i = 0; i < MAX_RTC_TEST; i++) + platform_device_put(pdev[i]); + + platform_driver_unregister(&test_driver); + return err; +} + +static void __exit test_exit(void) +{ + int i; + + for (i = 0; i < MAX_RTC_TEST; i++) + platform_device_unregister(pdev[i]); + + platform_driver_unregister(&test_driver); +} + +MODULE_AUTHOR("Alessandro Zummo "); +MODULE_DESCRIPTION("RTC test driver/device"); +MODULE_LICENSE("GPL v2"); + +module_init(test_init); +module_exit(test_exit); -- cgit v1.3.1