Linux Watchdog driver development
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Prajwal N" <prajwal.n@oss.qualcomm.com>
Cc: linux-watchdog@vger.kernel.org
Subject: Re: [RFC PATCH 2/2] drivers/watchdog: Add RISC-V SBI watchdog driver
Date: Sat, 03 Oct 2026 01:33:18 +0000	[thread overview]
Message-ID: <20261003013318.E74021F0089A@smtp.kernel.org> (raw)
In-Reply-To: <20261002163758.2919372-3-prajwal.n@oss.qualcomm.com>

Thank you for your contribution! Sashiko AI review found 9 potential issue(s) to consider:
- [High] watchdog: riscv_sbi_wdt: Probe returns success on MSI virtual IRQ failure
- [High] watchdog: riscv_sbi_wdt: Silent u32 overflow in timeout conversion allows premature resets
- [High] watchdog: riscv_sbi_wdt: MSI interrupts leak and Use-After-Free on unbind
- [Medium] watchdog: riscv_sbi_wdt: Missing wdt->dev and parent initializations
- [Medium] watchdog: riscv_sbi_wdt: WDIOF_PRETIMEOUT exposed without hardware support
- [Medium] watchdog: riscv_sbi_wdt: set_timeout fails to adjust pretimeout
- [Low] watchdog: riscv_sbi_wdt: Build failure due to non-existent fwnode member
- [Low] watchdog: riscv_sbi_wdt: Missing module_exit() prevents unloading
- [Low] watchdog: riscv_sbi_wdt: Build failure on configs missing GENERIC_MSI_IRQ
--

commit 5a455815a87582fc7be391ea2d8512e1bd8d0b90
Author: Prajwal N <prajwal.n@oss.qualcomm.com>

drivers/watchdog: Add RISC-V SBI watchdog driver

This commit adds support for the RISC-V SBI watchdog extension, integrating
with the Linux watchdog framework. It configures and controls the watchdog
via standard SBI calls and optionally supports pretimeout MSIs.

> diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
> --- a/drivers/watchdog/Kconfig
> +++ b/drivers/watchdog/Kconfig
[ ... ]
> +config RISCV_SBI_WATCHDOG
> +	tristate "RISC-V SBI based Watchdog support"
> +	depends on RISCV_SBI

[Severity: Low]
Does this configuration need a dependency on CONFIG_GENERIC_MSI_IRQ?

Without it, the unconditional call to
platform_device_msi_init_and_alloc_irqs() in probe will cause a build
failure when that config is disabled.

> diff --git a/drivers/watchdog/riscv_sbi_wdt.c b/drivers/watchdog/riscv_sbi_wdt.c
> new file mode 100644
> --- /dev/null
> +++ b/drivers/watchdog/riscv_sbi_wdt.c
[ ... ]
> +/* Convert second to microsecond */
> +static inline u32 sec_to_usec(unsigned int sec)
> +{
> +	return (u32)(sec * 1000000ULL);
> +}

[Severity: High]
Can this silently overflow if a user with privileges configures a large
timeout?

For example, setting a timeout greater than 4294 seconds overflows the u32
calculation, causing the hardware to receive an extremely short timeout
instead, potentially triggering an unexpected system reset.

[ ... ]
> +/* Set the watchdog timeout value */
> +static int sbiwdt_set_timeout(struct watchdog_device *wdd, unsigned int timeout)
> +{
> +	struct sbiwdt *wdt = watchdog_get_drvdata(wdd);
> +	u32 period_usec;
> +	int ret;
> +
> +	period_usec = sec_to_usec(timeout);
> +
> +	ret = sbiwdt_write_attribute(SBI_WDT_PERIOD, period_usec);
> +	if (ret)
> +		return ret;
> +
> +	wdt->attrs.wdt_period = period_usec;
> +	wdt->wdd.timeout = timeout;
> +
> +	return 0;
> +}

[Severity: Medium]
Does this function need to adjust or validate against wdd->pretimeout?

If a user sets a new timeout that is smaller than the current pretimeout,
the hardware could be left in an invalid state where the notification time
is greater than the period time.

[ ... ]
> +/* Get the watchdog timeleft before expiry */
> +static unsigned int sbiwdt_get_timeleft(struct watchdog_device *wdd)
> +{
> +	struct sbiwdt *wdt = watchdog_get_drvdata(wdd);
> +	unsigned int time_left_usec;
> +	int ret;
> +
> +	ret = sbiwdt_read_attribute(SBI_WDT_TIME_LEFT, &time_left_usec);
> +	if (ret) {
> +		dev_err(wdt->dev, "Failed to read watchdog time left: %d\n", ret);

[Severity: Medium]
Will this dereference an uninitialized wdt->dev pointer?

It appears wdt->dev is never assigned during probe, which would lead to
obscured error logs displaying "(NULL device *)".

> +		return 0;
> +	}
> +
> +	return usec_to_sec(time_left_usec);
> +}
> +
> +static const struct watchdog_info sbiwdt_info = {
> +	.options	=	WDIOF_SETTIMEOUT	|
> +				WDIOF_KEEPALIVEPING	|
> +				WDIOF_PRETIMEOUT	|
> +				WDIOF_MAGICCLOSE,

[Severity: Medium]
Should WDIOF_PRETIMEOUT only be advertised if the firmware actually
supports MSI capabilities?

Statically advertising it means userspace could configure a pretimeout that
will silently never trigger on platforms without MSI support.

[ ... ]
> +/* Initialize the watchdog attributes */
> +static int sbiwdt_init_attributes(struct sbiwdt *wdt)
> +{
[ ... ]
> +	/* Initialize the watchdog timeout to default value */
> +	timeout_usec = sec_to_usec(SBI_WDT_DEFAULT_TIMEOUT);
> +	ret = sbiwdt_write_attribute(SBI_WDT_PERIOD, timeout_usec);
> +	if (ret)
> +		return ret;
> +
> +	wdt->attrs.wdt_period = timeout_usec;
> +	wdt->wdd.timeout = SBI_WDT_DEFAULT_TIMEOUT;

[Severity: High]
Should wdt->wdd.max_timeout be explicitly initialized here?

Without an upper bound set by the driver, the watchdog core defaults to no
hardware limit, allowing large timeouts to be passed to sec_to_usec()
which then truncates them via integer overflow.

[ ... ]
> +static int sbiwdt_probe(struct platform_device *pdev)
> +{
> +	struct sbiwdt *wdt;
> +	struct imsic_global_config *imsic_global;
> +	struct irq_domain *msi_domain;
> +	struct device *dev = &pdev->dev;
> +	int rc;
> +
> +	/* Allocate memory for the global watchdog structure */
> +	wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
> +	if (!wdt)
> +		return -ENOMEM;
> +
> +	platform_set_drvdata(pdev, wdt);
> +	watchdog_set_drvdata(&wdt->wdd, wdt);

[Severity: Medium]
Are we missing assignments for wdt->dev and wdt->wdd.parent here?

Failing to set wdt->wdd.parent = dev breaks the sysfs hierarchy for the
watchdog device, and missing wdt->dev = dev leads to the uninitialized
pointer use observed in sbiwdt_get_timeleft().

[ ... ]
> +	/* Setup MSI if the capability is available */
> +	if (wdt->attrs.wdt_capability & SBI_WDT_CAP_MSI_SUPPORT_MASK) {
> +		imsic_global = imsic_get_global_config();
> +		if (!imsic_global) {
> +			dev_err(dev, "IMSIC firmware node is unavailable\n");
> +			return -ENODEV;
> +		}
> +
> +		msi_domain = irq_find_matching_fwnode(imsic_global->fwnode,
> +							DOMAIN_BUS_PLATFORM_MSI);

[Severity: Low]
Does struct imsic_global_config actually have an fwnode member?

It appears this member does not exist in the upstream struct definition,
which will cause an immediate build failure.

> +		if (!msi_domain) {
> +			dev_err(dev, "IMSIC platform MSI domain is unavailable\n");
> +			return -ENODEV;
> +		}
> +
> +		dev_set_msi_domain(&pdev->dev, msi_domain);
> +
> +		/* MSI setup*/
> +		rc = platform_device_msi_init_and_alloc_irqs(dev, 1, sbiwdt_msi_write);

[Severity: High]
Are these MSI allocations ever cleaned up if subsequent probe steps fail?

Since this is not a device-managed function, error paths that return early
will leak these interrupts.

> +		if (rc) {
> +			dev_err(dev, "Failed to allocate MSIs\n");
> +			return rc;
> +		}
> +
> +		wdt->virq = msi_get_virq(dev, 0);
> +		if (!wdt->virq) {
> +			dev_err(dev, "Failed to get MSI virtual IRQ: %d\n", wdt->virq);
> +			return wdt->virq;
> +		}

[Severity: High]
Will this return 0 (success) when it intends to return a failure?

msi_get_virq() returns 0 on failure. Returning wdt->virq here falsely
signals a successful probe to the driver core, leaving the system silently
unprotected since the watchdog never fully registers.

[ ... ]
> +static struct platform_driver sbiwdt_driver = {
> +	.probe  = sbiwdt_probe,
> +	.driver = {
> +			.name = DRV_NAME,
> +	},
> +};

[Severity: High]
Does this driver need a .remove callback to properly free the MSIs?

Without one, unbinding the driver permanently leaks the interrupts. If a
leaked MSI triggers later, it could execute sbiwdt_msi_write() causing a
use-after-free on the released driver data.

> +
> +static int __init sbiwdt_init(void)
> +{
> +	int ret;
> +	struct platform_device *pdev;
> +
> +	/* Probe for SBI WDT extension */
> +	if (sbi_spec_version < sbi_mk_version(3, 0) ||
> +		sbi_probe_extension(SBI_EXT_WDT) <= 0) {
> +		return -ENODEV;
> +	}
> +
> +	ret = platform_driver_register(&sbiwdt_driver);
> +	if (ret)
> +		return ret;
> +
> +	pdev = platform_device_register_simple(DRV_NAME, -1, NULL, 0);
> +	if (IS_ERR(pdev)) {
> +		platform_driver_unregister(&sbiwdt_driver);
> +		return PTR_ERR(pdev);
> +	}
> +
> +	return 0;
> +}
> +late_initcall(sbiwdt_init);

[Severity: Low]
Should a symmetric module_exit() routine be provided here?

Since the driver is configured as tristate, omitting an exit function
prevents it from ever being unloaded via rmmod.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261002163758.2919372-1-prajwal.n@oss.qualcomm.com?part=2

      reply	other threads:[~2026-10-03  1:33 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-02 16:37 [RFC PATCH 0/2] Add RISC-V SBI watchdog driver Prajwal N
2026-10-02 16:37 ` [RFC PATCH 1/2] include: sbi_ecall_interface: Add defines for the RISC-V SBI watchdog extension Prajwal N
2026-10-03  1:33   ` sashiko-bot
2026-10-02 16:37 ` [RFC PATCH 2/2] drivers/watchdog: Add RISC-V SBI watchdog driver Prajwal N
2026-10-03  1:33   ` sashiko-bot [this message]

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=20261003013318.E74021F0089A@smtp.kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=linux-watchdog@vger.kernel.org \
    --cc=prajwal.n@oss.qualcomm.com \
    --cc=sashiko-reviews@lists.linux.dev \
    /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