Devicetree
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: pankaj.gupta@oss.nxp.com
Cc: conor+dt@kernel.org, robh@kernel.org, devicetree@vger.kernel.org,
	Frank.Li@kernel.org, imx@lists.linux.dev
Subject: Re: [PATCH v27 5/7] firmware: drivers: imx: adds miscdev
Date: Wed, 15 Jul 2026 17:36:48 +0000	[thread overview]
Message-ID: <20260715173648.D6C7C1F000E9@smtp.kernel.org> (raw)
In-Reply-To: <20260715-imx-se-if-v27-5-bb7c45952f06@nxp.com>

Thank you for your contribution! Sashiko AI review found 13 potential issue(s) to consider:
- [High] The `read()` path drops `fops_lock` and `clbk_rx_lock` before accessing shared lists and `rx_msg`, leading to Use-After-Free and list corruption.
- [High] KASAN slab-out-of-bounds read in `se_val_rsp_hdr_n_status` due to insufficient response buffer size validation.
- [High] Indefinite hang during module unload or device unbind when userspace is blocked in `SE_IOCTL_CMD_SEND_RCV_RSP`.
- [High] Hung task panic triggered by signal interruption during long firmware waits in `ele_msg_rcv()`.
- [High] The `SE_IOCTL_CMD_SEND_RCV_RSP` ioctl silently swallows kernel-level validation errors, returning success to userspace.
- [High] Memory leak of dynamically allocated `se_buf_desc` structures when a device fd is closed without cleaning up pending buffers.
- [High] IOCTL error path zeroes out shared DMA memory buffers while the firmware might still be actively accessing them due to a timeout.
- [High] Fatal kernel crash during probe failure due to `dma_free_coherent` on an uninitialized pointer for the internal device context.
- [High] Unsynchronized clearing of `rx_msg_sz` in `se_if_fops_read()` causes lost commands and `-ENODEV` when interrupted by a signal.
- [High] TOCTOU race during device context creation in `open()` against unbind cleanup allows file operations on an unbound driver.
- [High] Race window during driver unbind between `cancel_work_sync()` and `se_clear_fw_busy()` allows work scheduling on freed memory.
- [Medium] Race condition exposing device node before probe initialization completes.
- [Medium] Mixing `goto`-based error handling with scope-based cleanup (`__free` and `guard`) violates kernel cleanup rules.
--

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

firmware: drivers: imx: adds miscdev

Adds the driver for communication interface to secure-enclave, that
enables exchanging messages with NXP secure enclave HW IP(s)
like EdgeLock Enclave, from User-Space Applications via character driver.

Link: https://lkml.kernel.org/r/20260629-imx-se-if-v26-0-146446285744@nxp.com?part=5

> diff --git a/drivers/firmware/imx/ele_common.c b/drivers/firmware/imx/ele_common.c
> index 955b7409ff9fc..7deff38c46132 100644
> --- a/drivers/firmware/imx/ele_common.c
> +++ b/drivers/firmware/imx/ele_common.c
> @@ -67,6 +122,9 @@ int ele_msg_rcv(struct se_if_device_ctx *dev_ctx, struct se_clbk_handle *se_clbk
>  			 * synchronized state.
>  			 */
>  			if (READ_ONCE(priv->waiting_rsp_clbk_hdl.dev_ctx)) {
> +				if (!is_rsp_wait_with_timeout)
> +					return ret;
> +
>  				WRITE_ONCE(se_clbk_hdl->signal_rcvd, true);
>  				wait_uninterruptible = true;
>  				continue;

[Severity: High]
Can this sequence trigger a hung task panic on systems with the hung task
watchdog enabled? 

In ele_msg_rcv(), if a signal interrupts the wait, the code sets 
wait_uninterruptible = true and continues waiting. Since the timeout for
a standard command response is set to 5000 seconds, wouldn't waiting in
TASK_UNINTERRUPTIBLE for this long reliably crash the system?

> diff --git a/drivers/firmware/imx/se_ctrl.c b/drivers/firmware/imx/se_ctrl.c
> index 25f9cd5441f90..fb82df5c58d56 100644
> --- a/drivers/firmware/imx/se_ctrl.c
> +++ b/drivers/firmware/imx/se_ctrl.c
> @@ -197,11 +204,234 @@ static int get_se_soc_info(struct se_if_priv *priv, const struct se_soc_info *se
>  	return 0;
>  }
>  
> +static void cleanup_se_shared_mem(struct se_if_device_ctx *dev_ctx)
> +{
> +	struct se_shared_mem_mgmt_info *se_shared_mem_mgmt = &dev_ctx->se_shared_mem_mgmt;
> +	struct se_if_priv *priv = dev_ctx->priv;
> +
> +	/* Free non-secure shared buffer. */
> +	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]
Could this leak memory by failing to free the se_buf_desc items in the
pending_in and pending_out lists when the device file descriptor is closed?

[Severity: High]
Can this cause a kernel crash during probe failure? 

If an internal command times out during probe, the internal context 
priv_dev_ctx might be marked fw_busy before shared memory is allocated 
(ptr is NULL). 

Calling dma_free_coherent() with a NULL pointer and a size of 65536 bytes 
could trigger a bug in the DMA subsystem.

> +static void se_dev_ctx_shared_mem_cleanup(struct se_if_device_ctx *dev_ctx)
> +{
> +	struct se_shared_mem_mgmt_info *se_shared_mem_mgmt = &dev_ctx->se_shared_mem_mgmt;
> +	struct list_head *pending_lists[] = {&se_shared_mem_mgmt->pending_in,
> +						&se_shared_mem_mgmt->pending_out};
> +	struct se_buf_desc *b_desc, *temp;
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(pending_lists); i++) {
> +		list_for_each_entry_safe(b_desc, temp, pending_lists[i], link) {
> +			if (b_desc->shared_buf_ptr)
> +				memset(b_desc->shared_buf_ptr, 0, b_desc->size);

[Severity: High]
Does this zero out the shared DMA memory buffers while the firmware might
still be actively accessing them? 

If ele_msg_send_rcv() times out, this cleanup is called on the error path. 
Modifying active DMA buffers or reusing them for the next command could 
corrupt firmware execution.

> +static void cleanup_dev_ctx(struct se_if_device_ctx *dev_ctx, bool is_fclose)
> +{
> +	scoped_guard(mutex, &dev_ctx->fops_lock) {
> +		if (dev_ctx->cleanup_done)
> +			goto exit;

[Severity: High]
Can this cause an indefinite hang during unbind? 

If userspace is blocked in ele_msg_rcv() waiting for a firmware response, 
it holds fops_lock via scoped_cond_guard in se_ioctl(). This cleanup path 
uninterruptibly blocks trying to acquire the same fops_lock, deadlocking 
the unbind thread until the firmware replies or the timeout expires.

[Severity: Medium]
Does this violate the kernel cleanup subsystem rules? 

Using a goto to jump completely out of a scoped_guard() block creates 
confusing ownership semantics and can lead to bugs. The same issue appears 
in ele_get_info() with __free(kfree).

> +static int se_ioctl_cmd_snd_rcv_rsp_handler(struct se_if_device_ctx *dev_ctx,
> +					    void __user *uarg)
> +{
[ ... ]
> +	if (cmd_snd_rcv_rsp_info.rx_buf_sz < sizeof(struct se_msg_hdr) ||
> +	    cmd_snd_rcv_rsp_info.rx_buf_sz > MAX_ALLOWED_RX_MSG_SZ) {
> +		se_ioctl_cmd_snd_rcv_cleanup(dev_ctx, uarg, &cmd_snd_rcv_rsp_info);
> +		return -EINVAL;
> +	}

[Severity: High]
Does this validation allow a user to allocate an rx_msg buffer of exactly
4 bytes? 

In se_val_rsp_hdr_n_status(), msg->data[0] (bytes 4-7) is unconditionally 
read. If the command bypasses size-mismatch validation (like 
ELE_DEBUG_DUMP_REQ), this could cause a KASAN out-of-bounds read.

> +	rsp_status_err =
> +		se_val_rsp_hdr_n_status(priv, rx_msg, tx_msg->header.command,
> +					cmd_snd_rcv_rsp_info.rx_buf_sz,
> +					tx_msg->header.ver == priv->if_defs->base_api_ver);
> +
> +	if (!rsp_status_err) {
> +		err = se_dev_ctx_cpy_out_data(dev_ctx);
> +		if (err < 0) {
> +			se_ioctl_cmd_snd_rcv_cleanup(dev_ctx, uarg, &cmd_snd_rcv_rsp_info);
> +			return err;
> +		}
> +	}

[Severity: High]
Is there a missing assignment to err here? 

If se_val_rsp_hdr_n_status() fails, rsp_status_err is populated but never 
assigned to err. The function falls through and returns the positive success 
byte count from ele_msg_send_rcv(), silently swallowing the validation error 
and misleading userspace.

> +static ssize_t se_if_fops_read(struct file *fp, char __user *buf, size_t size,
> +			       loff_t *ppos)
> +{
[ ... ]
> +	/* We may need to copy the output data to user before
> +	 * delivering the completion message.
> +	 */
> +	err = se_dev_ctx_cpy_out_data(dev_ctx);
> +	if (err < 0)
> +		goto exit;

[Severity: High]
Can this result in a use-after-free and list corruption? 

The fops_lock is dropped before the blocking wait in ele_msg_rcv(). When 
resuming, this code unconditionally accesses the pending_in and pending_out 
lists in se_dev_ctx_cpy_out_data() without reacquiring fops_lock. If the 
device was closed concurrently, the DMA memory and rx_msg could already be 
freed.

> +exit:
> +	priv->cmd_receiver_clbk_hdl.rx_msg_sz = 0;
> +	se_dev_ctx_shared_mem_cleanup(dev_ctx);
> +
> +	return err;
> +}

[Severity: High]
Does clearing rx_msg_sz locklessly here cause lost firmware commands? 

If the wait in ele_msg_rcv() is interrupted by a signal, this jumps to exit 
and clears the size. If a mailbox interrupt concurrently receives a message 
and sets the size, this will overwrite it with 0. When userspace restarts 
the read(), it will reject the valid message because rx_msg_sz is 0.

> +static int se_if_fops_open(struct inode *nd, struct file *fp)
> +{
[ ... ]
> +	if (misc_dev_ctx->cleanup_done) {
> +		err = -ENODEV;
> +		goto out_unlock_misc;
> +	}
> +
> +	priv->dev_ctx_mono_count++;
> +	err = init_device_context(priv, priv->dev_ctx_mono_count, &dev_ctx);

[Severity: High]
Is there a TOCTOU race here against unbind cleanup? 

The fops_lock is dropped right after checking cleanup_done, but before 
init_device_context() adds the new context to dev_ctx_list under modify_lock. 
If se_if_probe_cleanup() runs in between, it could unbind the driver, 
resulting in open() returning a file descriptor for a fully unbound device.

> +static void se_if_probe_cleanup(void *plat_dev)
> +{
[ ... ]
> +	cancel_work_sync(&priv->fw_busy_work);
> +	/*
> +	 * A timed-out synchronous command may have retained a dev_ctx through
> +	 * priv->fw_busy_dev_ctx even after the fd was closed and the context was
> +	 * removed from dev_ctx_list. If no late response arrived, release that
> +	 * retained context during driver teardown.
> +	 *
> +	 * se_clear_fw_busy() is idempotent and internally checks
> +	 * priv->fw_busy_dev_ctx under fw_busy_lock.
> +	 */
> +	se_clear_fw_busy(priv);

[Severity: High]
Could this race with a late mailbox interrupt to cause a use-after-free? 

cancel_work_sync() is called before se_clear_fw_busy() clears the fw_busy
state flag. If a mailbox interrupt arrives between these two calls, it will
see fw_busy is still set and call schedule_work() on the soon-to-be-freed
priv->fw_busy_work structure.

> @@ -312,15 +1449,22 @@ static int se_if_probe(struct platform_device *pdev)
>  		return dev_err_probe(dev, ret,
>  				    "Failed to init reserved memory region.");
>  	}
>  
> -	ret = init_misc_device_context(priv, 0, &priv->priv_dev_ctx);
> +	ret = init_misc_device_context(priv, 0, &priv->priv_dev_ctx, &se_if_fops);
>  	if (ret)
>  		return dev_err_probe(dev, ret,
>  				     "Failed[0x%x] to create device contexts.",

[Severity: Medium]
Could this expose the device node to userspace before probe initialization
completes? 

misc_register() is called inside init_misc_device_context(), but
se_if_probe() continues to initialize load_fw and SoC info after this returns.
If userspace immediately opens the device and sends a command, it could bypass
the se_load_firmware() step.

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

  reply	other threads:[~2026-07-15 17:36 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-15 18:09 [PATCH v27 0/7] firmware: imx: driver for NXP secure-enclave pankaj.gupta
2026-07-15 18:09 ` [PATCH v27 1/7] Documentation/firmware: add imx/se to other_interfaces pankaj.gupta
2026-07-15 17:25   ` sashiko-bot
2026-07-15 18:09 ` [PATCH v27 2/7] dt-bindings: arm: fsl: add imx-se-fw binding doc pankaj.gupta
2026-07-15 17:27   ` sashiko-bot
2026-07-15 18:09 ` [PATCH v27 3/7] firmware: imx: add driver for NXP EdgeLock Enclave pankaj.gupta
2026-07-15 17:36   ` sashiko-bot
2026-07-15 18:09 ` [PATCH v27 4/7] firmware: imx: device context dedicated to priv pankaj.gupta
2026-07-15 17:34   ` sashiko-bot
2026-07-15 18:09 ` [PATCH v27 5/7] firmware: drivers: imx: adds miscdev pankaj.gupta
2026-07-15 17:36   ` sashiko-bot [this message]
2026-07-15 18:09 ` [PATCH v27 6/7] arm64: dts: imx8ulp: add secure enclave node pankaj.gupta
2026-07-15 17:31   ` sashiko-bot
2026-07-15 18:09 ` [PATCH v27 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=20260715173648.D6C7C1F000E9@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