All of lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Tim Michals" <tcmichals@gmail.com>
Cc: linux-sunxi@lists.linux.dev, devicetree@vger.kernel.org,
	robh@kernel.org, conor+dt@kernel.org
Subject: Re: [PATCH v3 4/7] remoteproc: sunxi: Add Allwinner A523 XuanTie E906 driver
Date: Sun, 01 Nov 2026 19:24:20 +0000	[thread overview]
Message-ID: <sashiko-outbox-192595@kernel.org> (raw)
In-Reply-To: <20261101185735.762559-5-tcmichals@gmail.com>

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] remoteproc: sunxi: missing IRQ sync in sunxi_rproc_stop() races crash handler
- [High] remoteproc: sunxi: race and UAF in sunxi_rproc_vq_work() on notifyids
--

commit f895edd61f6cf2e80a5341959b347c747139fe48
Author: Tim Michals <tcmichals@gmail.com>

remoteproc: sunxi: Add Allwinner A523 XuanTie E906 driver

This commit adds a remoteproc driver for the XuanTie E906 RISC-V core on
Allwinner A523, A527, and T527 SoCs, handling power domains, clocks, resets,
SRAM mappings, memory-region carveouts, mailbox notifications, and crash
reporting.

> diff --git a/drivers/remoteproc/sunxi_rproc.c b/drivers/remoteproc/sunxi_rproc.c
> new file mode 100644
> index 0000000000000..1cee7110f0b8c
> --- /dev/null
> +++ b/drivers/remoteproc/sunxi_rproc.c
> @@ -0,0 +1,855 @@

[ ... ]

> +VISIBLE_IF_KUNIT
> +int sunxi_rproc_vq_notify(int id, void *ptr, void *data)
> +{
> +	KUNIT_STATIC_STUB_REDIRECT(sunxi_rproc_vq_notify, id, ptr, data);
> +	rproc_vq_interrupt(data, id);
> +	return 0;
> +}
> +EXPORT_SYMBOL_IF_KUNIT(sunxi_rproc_vq_notify);
> +
> +/* One channel carries all notifications: check every vring */
> +VISIBLE_IF_KUNIT
> +void sunxi_rproc_vq_work(struct work_struct *work)
> +{
> +	struct sunxi_rproc *priv = container_of(work, struct sunxi_rproc, vq_work);
> +
> +	idr_for_each(&priv->rproc->notifyids, sunxi_rproc_vq_notify, priv->rproc);
> +}
> +EXPORT_SYMBOL_IF_KUNIT(sunxi_rproc_vq_work);

[Severity: High]
As reported by Sashiko on v2 [1], in drivers/remoteproc/sunxi_rproc.c, can
sunxi_rproc_vq_work() race with rproc_resource_cleanup() during
rproc_shutdown(), rproc_boot_recovery(), or sunxi_rproc_remove() ->
rproc_del() and trigger a use-after-free?

Because priv->mbox_chan stays active across remoteproc stops, crash
recoveries, and throughout rproc_del() in sunxi_rproc_remove(), and neither
sunxi_rproc_stop() nor sunxi_rproc_unprepare() cancels priv->vq_work,
sunxi_rproc_vq_work() can run concurrently with rproc_resource_cleanup().

In sunxi_rproc_vq_work(), idr_for_each() iterates priv->rproc->notifyids
without holding priv->rproc->lock (unlike stm32_rproc_mb_vq_work() or
imx_dsp_rproc_vq_work()) and without RCU read-side protection (which cannot
be held across sunxi_rproc_vq_notify() because rproc_vq_interrupt() invokes
virtqueue callbacks that can sleep):

  lib/idr.c:idr_for_each()
    radix_tree_for_each_slot(slot, &idr->idr_rt, &iter, 0)
      fn(id, rcu_dereference_raw(*slot), data)

While sunxi_rproc_vq_notify() is executing, a concurrent teardown runs:

  rproc_resource_cleanup()
    platform_device_unregister(rvdev->pdev)
      rproc_virtio_remove()
        rproc_free_vring()
          idr_remove(&rproc->notifyids, rvring->notifyid)

If removing the vring frees the underlying struct radix_tree_node via
call_rcu(), the grace period can finish while sunxi_rproc_vq_notify() sleeps,
leaving radix_tree_next_slot() in idr_for_each() to dereference a freed slot
pointer.

In addition, although __rproc_virtio_del_vqs() clears rvring->vq and runs
synchronize_srcu(&rproc->vq_srcu) during rproc_stop_subdevices(), the rvring
entry stays in rproc->notifyids until platform_device_unregister(rvdev->pdev)
removes it and immediately frees the devm-allocated struct rproc_vdev
without another synchronize_srcu(&rproc->vq_srcu). If rproc_vq_interrupt()
looks up rvring in rproc->notifyids just before idr_remove(), it can
dereference rvring->vq after rvdev has been freed.

Should sunxi_rproc_vq_work() hold mutex_lock(&priv->rproc->lock) around
idr_for_each(), or should the driver synchronize and stop queuing vq_work
before rproc_resource_cleanup() tears down the vrings?

> +
> +/*
> + * Mailbox rx callbacks run in atomic context, but the virtio/rpmsg receive
> + * path takes mutexes, so the vring notifications are handled from a work item.
> + */
> +VISIBLE_IF_KUNIT
> +void sunxi_rproc_mb_rx_callback(struct mbox_client *cl, void *data)
> +{
> +	struct sunxi_rproc *priv = container_of(cl, struct sunxi_rproc, cl);
> +
> +	queue_work(priv->workqueue, &priv->vq_work);
> +}
> +EXPORT_SYMBOL_IF_KUNIT(sunxi_rproc_mb_rx_callback);
> +
> +VISIBLE_IF_KUNIT
> +irqreturn_t sunxi_rproc_crash_handler(int irq, void *data)
> +{
> +	struct sunxi_rproc *priv = data;
> +
> +	/*
> +	 * Stays masked until the next start(); recovery runs stop() first. A
> +	 * stop() that already disarmed it has disabled the line itself.
> +	 */
> +	if (test_and_clear_bit(SUNXI_RPROC_CRASH_IRQ_ARMED, &priv->flags))
> +		disable_irq_nosync(irq);
> +
> +	rproc_report_crash(priv->rproc, RPROC_FATAL_ERROR);
> +
> +	return IRQ_HANDLED;
> +}
> +EXPORT_SYMBOL_IF_KUNIT(sunxi_rproc_crash_handler);

[ ... ]

> +VISIBLE_IF_KUNIT
> +int sunxi_rproc_start(struct rproc *rproc)
> +{
> +	struct sunxi_rproc *priv = rproc->priv;
> +	const struct sunxi_rproc_cfg *cfg = priv->cfg;
> +	int ret;
> +
> +	dev_dbg(priv->dev, "Starting %s core at entry 0x%llx\n",
> +		cfg->name, (u64)rproc->bootaddr);
> +
> +	if (rproc->bootaddr > U32_MAX)
> +		return -EINVAL;
> +
> +	/* Program the reset vector and flush it before releasing the core */
> +	sunxi_rproc_writel((u32)rproc->bootaddr, priv->cfg_va + cfg->boot_reg_offset);
> +	sunxi_rproc_readl(priv->cfg_va + cfg->boot_reg_offset);
> +
> +	ret = reset_control_deassert(priv->rst_core);
> +	if (ret) {
> +		dev_err(priv->dev, "failed to release core reset: %d\n", ret);
> +		return ret;
> +	}
> +
> +	if (priv->crash_irq > 0 &&
> +	    !test_and_set_bit(SUNXI_RPROC_CRASH_IRQ_ARMED, &priv->flags))
> +		enable_irq(priv->crash_irq);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_IF_KUNIT(sunxi_rproc_start);
> +
> +static int sunxi_rproc_stop(struct rproc *rproc)
> +{
> +	struct sunxi_rproc *priv = rproc->priv;
> +	const struct sunxi_rproc_cfg *cfg = priv->cfg;
> +
> +	dev_dbg(priv->dev, "Halting %s core...\n", cfg->name);
> +
> +	/* Assert reset so the core stops executing */
> +	reset_control_assert(priv->rst_core);
> +
> +	if (priv->crash_irq > 0 &&
> +	    test_and_clear_bit(SUNXI_RPROC_CRASH_IRQ_ARMED, &priv->flags))
> +		disable_irq(priv->crash_irq);
> +
> +	return 0;
> +}

[Severity: High]
As reported by Sashiko on v2 [2], in drivers/remoteproc/sunxi_rproc.c, what
happens if sunxi_rproc_stop() runs right after sunxi_rproc_crash_handler()
clears SUNXI_RPROC_CRASH_IRQ_ARMED, while the threaded handler is still
executing?

Because sunxi_rproc_crash_handler() is a preemptible IRQ thread, it can clear
SUNXI_RPROC_CRASH_IRQ_ARMED and be preempted before calling
disable_irq_nosync(irq) or rproc_report_crash(priv->rproc, RPROC_FATAL_ERROR):

  CPU0 (IRQ thread)                    CPU1 (rproc_shutdown / recovery)
  sunxi_rproc_crash_handler()
    test_and_clear_bit(
      SUNXI_RPROC_CRASH_IRQ_ARMED,
      &priv->flags) -> true
                                       sunxi_rproc_stop()
                                         test_and_clear_bit(
                                           SUNXI_RPROC_CRASH_IRQ_ARMED,
                                           &priv->flags) -> false
                                         /* returns without waiting */

If a subsequent rproc_boot() calls sunxi_rproc_start() before CPU0 reaches
disable_irq_nosync(irq), sunxi_rproc_start() sets SUNXI_RPROC_CRASH_IRQ_ARMED
and calls enable_irq(priv->crash_irq) while the IRQ disable depth is still 0,
triggering an unbalanced enable warning in __enable_irq(). When CPU0 then
runs disable_irq_nosync(irq), the IRQ is left disabled while
SUNXI_RPROC_CRASH_IRQ_ARMED is set, and the next sunxi_rproc_stop() will
increment the disable depth to 2, leaving the crash IRQ permanently masked on
later boots.

Furthermore, because sunxi_rproc_stop() does not call
synchronize_irq(priv->crash_irq) when test_and_clear_bit() returns false, the
in-flight sunxi_rproc_crash_handler() thread can call rproc_report_crash()
after the stop (and a subsequent restart) has already completed.

Should sunxi_rproc_stop() still call synchronize_irq(priv->crash_irq) when
priv->crash_irq > 0 and SUNXI_RPROC_CRASH_IRQ_ARMED was already cleared by
the crash handler?

[1] https://lore.kernel.org/r/20260927003338.CB0E21F000FF@smtp.kernel.org
[2] https://lore.kernel.org/r/20260927003338.CB0E21F000FF@smtp.kernel.org

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261101185735.762559-1-tcmichals@gmail.com?part=4

  reply	other threads:[~2026-11-01 19:24 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-01 18:57 [PATCH v3 0/7] Add Allwinner A523 message box and XuanTie E906 remoteproc support Tim Michals
2026-11-01 18:57 ` [PATCH v3 1/7] dt-bindings: mailbox: Add Allwinner A523 message box Tim Michals
2026-11-01 19:06   ` sashiko-bot
2026-11-02 10:17   ` Krzysztof Kozlowski
2026-11-01 18:57 ` [PATCH v3 2/7] mailbox: sun55i: Add Allwinner A523 message box driver Tim Michals
2026-11-01 19:17   ` sashiko-bot
2026-11-01 18:57 ` [PATCH v3 3/7] dt-bindings: remoteproc: Add Allwinner A523 RISC-V remote processor Tim Michals
2026-11-02 10:19   ` Krzysztof Kozlowski
2026-11-01 18:57 ` [PATCH v3 4/7] remoteproc: sunxi: Add Allwinner A523 XuanTie E906 driver Tim Michals
2026-11-01 19:24   ` sashiko-bot [this message]
2026-11-01 18:57 ` [PATCH v3 5/7] arm64: dts: allwinner: a523: Add message box and remoteproc nodes Tim Michals
2026-11-01 18:57 ` [PATCH v3 6/7] mailbox: sun55i: Add KUnit tests Tim Michals
2026-11-01 18:57 ` [PATCH v3 7/7] remoteproc: sunxi: " Tim Michals
2026-11-01 19:13   ` sashiko-bot

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=sashiko-outbox-192595@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=linux-sunxi@lists.linux.dev \
    --cc=robh@kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=tcmichals@gmail.com \
    /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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.