Devicetree
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: pankaj.gupta@oss.nxp.com
Cc: imx@lists.linux.dev, Frank.Li@kernel.org, conor+dt@kernel.org,
	devicetree@vger.kernel.org, robh@kernel.org
Subject: Re: [PATCH v29 5/7] firmware: drivers: imx: adds miscdev
Date: Tue, 21 Jul 2026 10:54:48 +0000	[thread overview]
Message-ID: <20260721105449.200D91F000E9@smtp.kernel.org> (raw)
In-Reply-To: <20260721-imx-se-if-v29-5-04a362f4fcca@nxp.com>

Thank you for your contribution! Sashiko AI review found 10 potential issue(s) to consider:
- [Critical] Arbitrary physical memory read/write vulnerability (Confused Deputy) via SE_IOCTL_CMD_SEND_RCV_RSP.
- [High] Use-After-Free of `gate` and `miscdev` due to race between unbind and VFS `open`.
- [High] Unbind deadlock/hang for up to 5000 seconds due to incorrect ordering of `complete_all` and `fops_lock` acquisition.
- [High] Overwriting a successful firmware transaction with `-ERESTARTSYS` leads to double execution with zeroed DMA buffers.
- [High] NULL pointer dereference in `se_if_rx_callback` due to clearing `drvdata` while the mailbox channel is active.
- [High] Out-of-bounds read in `se_if_rx_callback` by trusting unvalidated firmware payload length.
- [High] Premature freeing of `gen_pool` DMA buffer on timeout risks silent memory corruption.
- [Medium] Lockdep false positive 'possible recursive locking detected' on `fops_lock`.
- [Medium] Early device exposure to userspace before probe initialization is complete.
- [Low] Implicit compiler padding in UAPI structure `se_ioctl_cmd_snd_rcv_rsp_info`.
--

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

This patch adds the driver for a communication interface to the NXP
secure-enclave. It exposes a character device to userspace for exchanging
messages with hardware IPs like EdgeLock Enclave. It also implements
command validation, shared memory management, and power/reset command
restrictions.

> diff --git a/include/uapi/linux/se_ioctl.h b/include/uapi/linux/se_ioctl.h
> new file mode 100644
> index 0000000000000..ea14cec05020d
> --- /dev/null
> +++ b/include/uapi/linux/se_ioctl.h
> @@ -0,0 +1,97 @@
[ ... ]
> +struct se_ioctl_cmd_snd_rcv_rsp_info {
> +	__u64 tx_buf;
> +	__u32 tx_buf_sz;
> +	__u64 rx_buf;
> +	__u32 rx_buf_sz;
> +};

[Severity: Low]
Will this struct have implicit compiler padding after the 32-bit fields,
potentially causing UAPI size differences across architectures?

> diff --git a/drivers/firmware/imx/ele_base_msg.c b/drivers/firmware/imx/ele_base_msg.c
> index 524ebc0f697e6..28b835f2880fb 100644
> --- a/drivers/firmware/imx/ele_base_msg.c
> +++ b/drivers/firmware/imx/ele_base_msg.c
> @@ -15,24 +15,65 @@
[ ... ]
> +static void ele_get_info_cleanup(struct se_if_priv *priv, u32 *buf, dma_addr_t d_addr,
> +				 size_t size)
> +{
[ ... ]
> +	if (priv->mem_pool)
> +		gen_pool_free(priv->mem_pool, (unsigned long)buf, size);
> +	else if (!se_is_fw_busy_ctx(priv->priv_dev_ctx))
> +		se_dev_ctx_shared_mem_cleanup(priv->priv_dev_ctx);
> +}

[Severity: High]
Could the gen_pool_free() path free memory while the hardware is still busy?
The standard allocation path uses a se_is_fw_busy_ctx() guard to prevent
freeing memory the hardware might still write to, but that guard is missing
here, risking silent memory corruption if a timeout occurred.

> diff --git a/drivers/firmware/imx/ele_common.c b/drivers/firmware/imx/ele_common.c
> index a350e83a274a1..fbb6e584341a1 100644
> --- a/drivers/firmware/imx/ele_common.c
> +++ b/drivers/firmware/imx/ele_common.c
> @@ -226,6 +281,7 @@ static bool check_hdr_exception_for_sz(struct se_if_priv *priv,
>  void se_if_rx_callback(struct mbox_client *mbox_cl, void *msg)
>  {
>  	struct se_clbk_handle *se_clbk_hdl;
> +	bool schedule_fw_busy_work = false;
>  	struct device *dev = mbox_cl->dev;
>  	struct se_msg_hdr *header;
>  	bool sz_mismatch = false;
> @@ -305,9 +361,13 @@ void se_if_rx_callback(struct mbox_client *mbox_cl, void *msg)
[ ... ]
>  	priv = dev_get_drvdata(dev);
>  
>  	/* The function can be called with NULL msg */
>  	if (IS_ERR_OR_NULL(msg)) {
[ ... ]
>  	header = msg;
>  	rx_msg_sz = header->size << 2;
>  
>  	/* Incoming command: wake up the receiver if any. */
>  	if (header->tag == priv->if_defs->cmd_tag) {

[Severity: High]
Can priv be NULL here if the callback fires during driver unbind?
In se_if_probe_cleanup(), dev_set_drvdata() clears the private data pointer
before devres frees the active mailbox channels. A late interrupt would
dereference the NULL pointer.

> [ ... ]
>  		se_clbk_hdl->rx_msg_sz = rx_msg_sz;
>  		memcpy(se_clbk_hdl->rx_msg, msg, se_clbk_hdl->rx_msg_sz);
>  		complete(&se_clbk_hdl->done);

[Severity: High]
Does this blindly trust the firmware-provided header->size for the copy?
If the mailbox driver's msg buffer is smaller than rx_msg_sz, this could
result in an out-of-bounds read that copies arbitrary kernel memory into
the response buffer returned to userspace.

> diff --git a/drivers/firmware/imx/se_ctrl.c b/drivers/firmware/imx/se_ctrl.c
> index cd4d27dd98ce1..3c14964830a6d 100644
> --- a/drivers/firmware/imx/se_ctrl.c
> +++ b/drivers/firmware/imx/se_ctrl.c
> @@ -243,11 +250,269 @@ static int get_se_soc_info(struct se_if_priv *priv, const struct se_soc_info *se
[ ... ]
> +static int load_firmware(struct se_if_priv *priv, const u8 *se_img_file_to_load)
> +{
[ ... ]
> +	/*
> +	 * Serialize access to priv_dev_ctx shared memory to prevent pos
> +	 * corruption if two driver-internal callers run concurrently (e.g.
> +	 * ele_get_info() racing with load_firmware()).
> +	 */
> +	scoped_guard(mutex, &priv->priv_dev_ctx->fops_lock) {
> +		se_fw_buf_len = fw->size;

[Severity: Medium]
Could this trigger a lockdep warning for recursive locking? The ioctl path
already holds a fops_lock, and taking the internal context's lock of the
same class here is valid hierarchical locking but might be flagged as a
deadlock by static analysis.

> @@ -261,18 +526,909 @@ static int init_misc_device_context(struct se_if_priv *priv, int ch_id,
[ ... ]
> +static int se_ioctl_cmd_snd_rcv_rsp_handler(struct se_if_device_ctx *dev_ctx,
> +					    void __user *uarg)
> +{
[ ... ]
> +	err = ele_msg_send_rcv(dev_ctx, tx_msg, cmd_snd_rcv_rsp_info.tx_buf_sz,
> +			       rx_msg, cmd_snd_rcv_rsp_info.rx_buf_sz);
> +	if (err < 0) {
> +		se_ioctl_cmd_snd_rcv_cleanup(dev_ctx, uarg, &cmd_snd_rcv_rsp_info);
> +
> +		return err;
> +	}

[Severity: Critical]
Does this allow arbitrary physical memory read/write vulnerabilities? Since
the earlier se_chk_tx_msg_hdr() check only verifies the UAPI command header,
user-controlled payloads containing embedded physical DMA addresses are sent
directly to the hardware here without validation. An attacker could forge
addresses to overwrite kernel memory.

[Severity: High]
If ele_msg_send_rcv() successfully completed a transaction on the hardware
but was interrupted by a signal, it overwrites the success with -ERESTARTSYS.
Does returning that negative error here cause issues? This triggers the
se_ioctl_cmd_snd_rcv_cleanup() handler which zeros the shared DMA input
buffers. The VFS will then restart the ioctl and execute a second time with
all-zero input parameters.

> +static int se_if_fops_open(struct inode *nd, struct file *fp)
> +{
> +	struct miscdevice *miscdev = fp->private_data;
> +	struct se_if_open_gate *gate;
> +	struct se_if_device_ctx *misc_dev_ctx;
> +	struct se_if_device_ctx *dev_ctx;
> +	struct se_if_priv *priv;
> +	int err = 0;
> +
> +	gate = container_of(miscdev, struct se_if_open_gate, miscdev);
> +
> +	if (!se_if_open_gate_get(gate))
> +		return -ENODEV;

[Severity: High]
Is there a use-after-free race here against module removal?
During unbind, se_if_probe_cleanup() calls misc_deregister() and frees
the gate structure. This does not synchronize with concurrent open() calls
that have already resolved the device in the VFS, leading to a dereference
of the freed miscdev and gate when the delayed fops->open runs.

> @@ -310,6 +1467,67 @@ static void se_if_probe_cleanup(void *plat_dev)
[ ... ]
> +	while (true) {
> +		dev_ctx = NULL;
> +
> +		scoped_guard(mutex, &priv->modify_lock) {
> +			if (list_empty(&priv->dev_ctx_list))
> +				goto out_done;
> +
> +			dev_ctx = list_first_entry(&priv->dev_ctx_list,
> +						   struct se_if_device_ctx, link);
> +
> +			/* pin this context so close() cannot free it under us */
> +			kref_get(&dev_ctx->refcount);
> +			dlink_dev_ctx(dev_ctx);
> +		}
> +
> +		/*
> +		 * Local cleanup outside the global lock avoids ABBA deadlock
> +		 * with paths that already take dev_ctx->fops_lock first.
> +		 */
> +		cleanup_dev_ctx(dev_ctx, false);
> +		kref_put(&dev_ctx->refcount, se_if_dev_ctx_release);
> +	}
> +out_done:
> +
> +	/*
> +	 * 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.
> +	 */
> +	complete_all(&priv->waiting_rsp_clbk_hdl.done);

[Severity: High]
Will this cause a deadlock during driver unbind?
If an ioctl thread is blocked waiting for the done completion, it currently
holds dev_ctx->fops_lock. Calling cleanup_dev_ctx() here attempts to acquire
that same lock and blocks indefinitely, because the complete_all() intended
to wake the ioctl thread is placed after this loop.

> @@ -418,13 +1653,12 @@ static int se_if_probe(struct platform_device *pdev)
[ ... ]
> -	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.",
>  				     ret);
>  
> -	/* By default, there is no pending FW to be loaded.*/
>  	if (se_info->imem_state_mgmt) {
>  		load_fw = get_load_fw_instance(priv);
>  
[ ... ]
>  	if (if_node->if_defs.se_if_type == SE_TYPE_ID_HSM) {
>  		ret = get_se_soc_info(priv, se_info);

[Severity: Medium]
Could exposing the miscdevice to userspace this early lead to TOCTOU races?
The device is registered before get_se_soc_info() is queried or the IMEM
DMA buffer is allocated, allowing userspace to open the device and interact
with uninitialized state.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260721-imx-se-if-v29-0-04a362f4fcca@nxp.com?part=5

  reply	other threads:[~2026-07-21 10:54 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-21 11:23 [PATCH v29 0/7] firmware: imx: driver for NXP secure-enclave pankaj.gupta
2026-07-21 11:23 ` [PATCH v29 1/7] Documentation/firmware: add imx/se to other_interfaces pankaj.gupta
2026-07-21 10:37   ` sashiko-bot
2026-07-21 11:23 ` [PATCH v29 2/7] dt-bindings: arm: fsl: add imx-se-fw binding doc pankaj.gupta
2026-07-21 11:23 ` [PATCH v29 3/7] firmware: imx: add driver for NXP EdgeLock Enclave pankaj.gupta
2026-07-21 10:49   ` sashiko-bot
2026-07-21 10:50   ` Frieder Schrempf
2026-07-21 11:23 ` [PATCH v29 4/7] firmware: imx: device context dedicated to priv pankaj.gupta
2026-07-21 10:51   ` sashiko-bot
2026-07-21 11:23 ` [PATCH v29 5/7] firmware: drivers: imx: adds miscdev pankaj.gupta
2026-07-21 10:54   ` sashiko-bot [this message]
2026-07-21 11:23 ` [PATCH v29 6/7] arm64: dts: imx8ulp: add secure enclave node pankaj.gupta
2026-07-21 10:57   ` sashiko-bot
2026-07-21 11:23 ` [PATCH v29 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=20260721105449.200D91F000E9@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