Linux kernel and device drivers for NXP i.MX platforms
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: pankaj.gupta@oss.nxp.com
Cc: devicetree@vger.kernel.org, imx@lists.linux.dev,
	conor+dt@kernel.org, robh@kernel.org, Frank.Li@kernel.org
Subject: Re: [PATCH v30 5/7] firmware: drivers: imx: adds miscdev
Date: Fri, 24 Jul 2026 08:32:21 +0000	[thread overview]
Message-ID: <20260724083221.CA7CC1F000E9@smtp.kernel.org> (raw)
In-Reply-To: <20260724-imx-se-if-v30-5-ce8ba256692c@nxp.com>

Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider:

New issues:
- [Critical] Driver unbind forcefully wakes waiting ioctls and clears firmware busy state, causing DMA Use-After-Free by the hardware.
- [High] Late mailbox interrupts can schedule `fw_busy_work` after it has been canceled, leading to a Use-After-Free of `priv` when the workqueue executes.
- [High] Unbind deadlock/hang due to concurrent ioctl threads blocked on `se_if_cmd_lock` bypassing the `complete_all()` wake-up.
- [Medium] The 128KB DMA shared memory buffer allocated for the internal `priv_dev_ctx` is permanently leaked on device unbind.

Pre-existing issues:
- [High] Concurrency race between se_if_rx_callback and se_if_fops_read on the shared command receiver buffer (rx_msg) leading to data corruption.
- [High] Dangling pointer priv->dev is passed to dma_free_coherent() and dev_warn() if a file descriptor is closed after device unbind.
--

--- Patch [5]: [PATCH v30 5/7] firmware: drivers: imx: adds miscdev ---
commit c7550c8a49060a4114eb071db0048e14879f71ab
Author: Pankaj Gupta <pankaj.gupta@nxp.com>

    firmware: drivers: imx: adds miscdev

This commit adds a miscdev character driver for the i.MX secure enclave to
allow user-space applications to exchange messages with NXP secure enclave
hardware. It implements ABI documentation, device contexts, and command
header validation to restrict unsupported operations.

> diff --git a/drivers/firmware/imx/ele_common.c b/drivers/firmware/imx/ele_common.c
> --- a/drivers/firmware/imx/ele_common.c
> +++ b/drivers/firmware/imx/ele_common.c
[ ... ]
> @@ -219,6 +219,9 @@ int ele_msg_send_rcv(struct se_if_device_ctx *dev_ctx, void *tx_msg, int tx_msg_
>  		return err;
>  	}
>  
>  	guard(mutex)(&priv->se_if_cmd_lock);
>  
>  	if (atomic_read(&priv->fw_busy)) {
>  		dev_dbg(priv->dev, "%s: ELE became unresponsive.\n", dev_ctx->devname);
>  		return -EBUSY;
>  	}
>  	reinit_completion(&priv->waiting_rsp_clbk_hdl.done);
>  	spin_lock_irqsave(&priv->waiting_rsp_clbk_hdl.clbk_rx_lock, flags);

[Severity: High]
Does this create an unbind deadlock if multiple ioctls are running concurrently?

When se_if_probe_cleanup() runs, it calls complete_all() to wake up the blocked
waiter in ele_msg_rcv(). However, concurrent firmware transactions are serialized
by priv->se_if_cmd_lock.

If Thread A holds se_if_cmd_lock and is blocked in ele_msg_rcv(), and Thread B is
blocked waiting to acquire se_if_cmd_lock, Thread B will miss the complete_all()
signal. Once Thread A finishes and releases the lock, Thread B will acquire it,
call reinit_completion() here, and wait for the full timeout. Meanwhile, the
unbind thread will block indefinitely trying to acquire dev_ctx->fops_lock
(which Thread B already holds from se_ioctl).

[ ... ]
> @@ -311,6 +314,10 @@ void se_if_rx_callback(struct mbox_client *mbox_cl, void *msg)
>  		/*
>  		 * Clamp the copy length to the pre-allocated receiver buffer (MAX_NVM_MSG_LEN).
>  		 */
>  		se_clbk_hdl->rx_msg_sz = min_t(u32, rx_msg_sz, MAX_NVM_MSG_LEN);
>  		devname = se_clbk_hdl->dev_ctx->devname;
>  		memcpy(se_clbk_hdl->rx_msg, msg, se_clbk_hdl->rx_msg_sz);
>  		complete(&se_clbk_hdl->done);
>  		spin_unlock_irqrestore(&se_clbk_hdl->clbk_rx_lock, flags);

[Severity: High]
This is a pre-existing issue, but can this lead to data corruption when
userspace is actively reading the response?

When se_if_fops_read() wakes up, it acquires clbk_rx_lock to snapshot the
pointers, then drops the lock and calls copy_to_user(). If the firmware sends
another command message while copy_to_user() is running, se_if_rx_callback()
will blindly overwrite the shared rx_msg buffer here via memcpy() since it
doesn't check if the buffer is currently being consumed.

[ ... ]
> @@ -354,6 +361,9 @@ void se_if_rx_callback(struct mbox_client *mbox_cl, void *msg)
>  		se_clbk_hdl = &priv->waiting_rsp_clbk_hdl;
>  		spin_lock_irqsave(&se_clbk_hdl->clbk_rx_lock, flags);
>  		if (!se_clbk_hdl->rx_msg) {
>  			if (atomic_read(&priv->fw_busy))
>  				schedule_fw_busy_work = true;
>  			spin_unlock_irqrestore(&se_clbk_hdl->clbk_rx_lock, flags);
>  
>  			if (schedule_fw_busy_work)
>  				schedule_work(&priv->fw_busy_work);

[Severity: High]
Can a late mailbox interrupt cause a Use-After-Free of priv by scheduling this
work after the driver has been unbound?

In se_if_probe_cleanup(), cancel_work_sync(&priv->fw_busy_work) is called
before the mailbox channels are freed (mbox_free_channel). If a late mailbox
message arrives right before or during the unbind process, this callback can
evaluate the condition and schedule the work after it was supposedly canceled,
leading to the workqueue accessing freed memory when the unbind completes.

> diff --git a/drivers/firmware/imx/se_ctrl.c b/drivers/firmware/imx/se_ctrl.c
> --- a/drivers/firmware/imx/se_ctrl.c
> +++ b/drivers/firmware/imx/se_ctrl.c
[ ... ]
> @@ -410,6 +410,9 @@ static void cleanup_se_shared_mem(struct se_if_device_ctx *dev_ctx)
>  	 * happen before the DMA backing memory is released to avoid a leak.
>  	 */
>  	se_dev_ctx_shared_mem_cleanup(dev_ctx);
>  
>  	dma_free_coherent(priv->dev, MAX_DATA_SIZE_PER_USER,
>  			  se_shared_mem_mgmt->non_secure_mem.ptr,
>  			  se_shared_mem_mgmt->non_secure_mem.dma_addr);

[Severity: High]
This is a pre-existing issue, but could this pass a dangling priv->dev
pointer to dma_free_coherent() if a file descriptor is closed after unbind?

The driver stores a pointer to the platform device in priv->dev in se_if_probe()
but does not pin it with get_device(). When the device is unbound, the
miscdevice is deregistered, dropping the VFS reference to the parent device.
If userspace still holds an open file descriptor, priv is kept alive, but the
underlying struct device might be freed. When close() is finally called, this
uses the dangling pointer.

[ ... ]
> @@ -650,6 +653,10 @@ static void se_if_priv_release(struct kref *kref)
>  
>  	/* Free priv_dev_ctx if it exists */
>  	if (priv->priv_dev_ctx) {
>  		/*
>  		 * miscdev storage belongs to open_gate, not directly to
>  		 * priv_dev_ctx. The gate should already have been detached
>  		 * from priv during teardown.
>  		 */
>  		cleanup_dev_ctx(priv->priv_dev_ctx, false);
>  
>  		kfree(priv->priv_dev_ctx->devname);
>  		kfree(priv->priv_dev_ctx);
>  		priv->priv_dev_ctx = NULL;
>  	}

[Severity: Medium]
Does this leak the 128KB DMA buffer allocated for priv_dev_ctx during unbind?

When se_if_probe_cleanup() runs, it sets priv->priv_dev_ctx->cleanup_done = true.
When se_if_priv_release() later calls cleanup_dev_ctx(priv->priv_dev_ctx, false),
cleanup_dev_ctx() skips freeing the DMA memory via cleanup_se_shared_mem()
because it sees cleanup_done is already true. The context struct is freed, but
the DMA memory is permanently leaked.

[ ... ]
> @@ -685,6 +692,10 @@ static void se_clear_fw_busy(struct se_if_priv *priv)
>  
>  	dev_ctx = priv->fw_busy_dev_ctx;
>  	priv->fw_busy_dev_ctx = NULL;
>  	atomic_set(&priv->fw_busy, 0);
>  	spin_unlock_irqrestore(&priv->fw_busy_lock, flags);
>  
>  	if (!dev_ctx)
>  		return;
>  
>  	scoped_guard(mutex, &dev_ctx->fops_lock) {
>  		if (dev_ctx->cleanup_done)
>  			cleanup_se_shared_mem(dev_ctx);
>  	}

[Severity: Critical]
Is it safe to clear the circuit breaker and unconditionally free the DMA memory
for legitimately timed-out contexts here?

During teardown, se_if_probe_cleanup() forcefully wakes up any waiting ioctls
using complete_all(). The woken thread in ele_msg_rcv() observes a positive
remaining timeout, thinks it succeeded, and thus skips setting the fw_busy
circuit breaker. It then proceeds to free the DMA memory.

Furthermore, se_clear_fw_busy() is called at the end of unbind, unconditionally
clearing the busy flag and freeing DMA memory for any context that timed out
previously. In both cases, the hardware enclave might still be executing and
actively DMA-writing to that freed physical memory, causing memory corruption.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260724-imx-se-if-v30-0-ce8ba256692c@nxp.com?part=5

  reply	other threads:[~2026-07-24  8:32 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-24  9:05 [PATCH v30 0/7] firmware: imx: driver for NXP secure-enclave pankaj.gupta
2026-07-24  9:05 ` [PATCH v30 1/7] Documentation/firmware: add imx/se to other_interfaces pankaj.gupta
2026-07-24  8:18   ` sashiko-bot
2026-07-24  9:05 ` [PATCH v30 2/7] dt-bindings: arm: fsl: add imx-se-fw binding doc pankaj.gupta
2026-07-24  9:05 ` [PATCH v30 3/7] firmware: imx: add driver for NXP EdgeLock Enclave pankaj.gupta
2026-07-24  8:28   ` sashiko-bot
2026-07-24  9:05 ` [PATCH v30 4/7] firmware: imx: device context dedicated to priv pankaj.gupta
2026-07-24  8:30   ` sashiko-bot
2026-07-24  9:05 ` [PATCH v30 5/7] firmware: drivers: imx: adds miscdev pankaj.gupta
2026-07-24  8:32   ` sashiko-bot [this message]
2026-07-24  9:05 ` [PATCH v30 6/7] arm64: dts: imx8ulp: add secure enclave node pankaj.gupta
2026-07-24  8:31   ` sashiko-bot
2026-07-24  9:05 ` [PATCH v30 7/7] arm64: dts: imx8ulp-evk: add reserved memory property pankaj.gupta

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=20260724083221.CA7CC1F000E9@smtp.kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=Frank.Li@kernel.org \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=imx@lists.linux.dev \
    --cc=pankaj.gupta@oss.nxp.com \
    --cc=robh@kernel.org \
    --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