RE: [PATCH v31 5/7] firmware: imx: adds miscdev

"Pankaj Gupta (OSS)" <[email protected]> Wed, 29 Jul 2026 17:48:47 +0000
Newsgroups dev.linux.lists.imx,dev.linux.lists.sashiko-reviews,org.kernel.vger.linux-devicetree
Message-ID <GV2PR04MB12271A86286CA15359602CA7A95CA2@GV2PR04MB12271.eurprd04.prod.outlook.com>
> > 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
> > @@ -246,6 +246,14 @@ int ele_msg_send_rcv(struct se_if_device_ctx
> *dev_ctx, void *tx_msg,
> >  	guard(mutex)(&priv->se_if_cmd_lock);
> >
> > +	/*
> > +	 * Teardown has begun: do not arm a new transaction. A thread that
> was
> > +	 * blocked on se_if_cmd_lock while se_if_probe_cleanup() ran its
> > +	 * complete_all() would otherwise miss that wake-up, re-arm the
> > +	 * completion below and wait the full timeout, while unbind blocks
> > +	 * indefinitely on this thread's fops_lock. Bail out instead.
> > +	 */
> > +	if (atomic_read(&priv->going_away))
> > +		return -ENODEV;
> > +
> >  	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);
> 
> [Severity: High]
> Is there a TOCTOU race condition here with se_if_probe_cleanup()? If
> se_if_probe_cleanup() sets going_away = 1 and calls complete_all() without
> acquiring se_if_cmd_lock, could it execute complete_all() right after this
> atomic_read() check but before reinit_completion() is called? Would this erase
> the forced teardown wakeup and cause a permanent deadlock?
> 
> [ ... ]

[Rejected]
TOCTOU race between ele_msg_send_rcv() checking going_away
and se_if_probe_cleanup() forcing wakeup (lost wakeup / deadlock):
No change is needed.
 
The ordering is already safe: se_if_probe_cleanup() sets
going_away before complete_all() (with an explicit ordering comment),
ele_msg_send_rcv() reads going_away under se_if_cmd_lock and bails with
-ENODEV before reinit_completion(), and ele_msg_rcv() re-checks
going_away after the wake and returns -ENODEV so a teardown-forced
completion is never mistaken for a real response. No code change.

> 
> > @@ -279,6 +284,14 @@ int ele_msg_send_rcv(struct se_if_device_ctx
> *dev_ctx, void *tx_msg,
> >  	if (priv->waiting_rsp_clbk_hdl.signal_rcvd) {
> >  		/*
> >  		 * Signal was deferred until the FW/kernel protocol
> resynchronized.
> >  		 * On success report -ERESTARTSYS for the interrupted wait;
> the
> >  		 * command is not re-sent. Keep real errors like -ETIMEDOUT.
> >  		 */
> >  		if (err > 0)
> >  			err = -ERESTARTSYS;
> >  		priv->waiting_rsp_clbk_hdl.signal_rcvd = false;
> 
> [Severity: High]
> This isn't a bug introduced by this patch, but does this explicit override of a
> successful hardware interaction with -ERESTARTSYS break Power Management
> transitions? If a signal is pending during system suspend, could this cause
> internal kernel hardware operations like se_save_imem_state() to fail
> spuriously and permanently lose the enclave IMEM state required for proper
> resume?
> 
> [ ... ]
Accepted, will be fixed in next respin.

@@ -100,22 +100,10 @@ int ele_msg_rcv(struct se_if_device_ctx *dev_ctx, struct se_clbk_handle *se_clbk
        if (se_clbk_hdl == &priv->waiting_rsp_clbk_hdl) {
                is_rsp_wait_with_timeout = true;
                deadline_jiffies = jiffies + remaining_jiffies;
+               if (se_clbk_hdl->dev_ctx == priv->priv_dev_ctx)
+                       wait_uninterruptible = true;
        }
> 
> > @@ -323,9 +395,13 @@ 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);
> 
> [Severity: High]
> Does this unconditionally overwrite the shared rx_msg buffer and call
> complete() upon receiving a firmware command? If the firmware issues a
> second command before the userspace daemon executes a read() to consume
> the first one, could the first command's payload be completely overwritten,
> resulting in a spurious -ENODEV failure?
> 
Rejected.

Firmware commands to the command receiver can overwrite an
unprocessed command (se_if_rx_callback() cmd_tag branch memcpy+complete
unconditionally): NO-CHANGE.
The NVM command-receiver protocol is half-duplex:
the firmware issues one storage/NVM command at a time and
waits for the userspace daemon's response before issuing the next, so
there is no in-flight command to overwrite. The receiver uses a
dedicated long-lived buffer and clbk_rx_lock already serialises the
callback against se_if_fops_read().

No code change.

> > 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
> > @@ -284,11 +292,308 @@ static int get_se_soc_info(struct se_if_priv
> > *priv, const struct se_soc_info *se
> > +static int init_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;
> > +
> > +	INIT_LIST_HEAD(&se_shared_mem_mgmt->pending_out);
> > +	INIT_LIST_HEAD(&se_shared_mem_mgmt->pending_in);
> > +
> > +	if (priv->mem_pool)
> > +		INIT_LIST_HEAD(&se_shared_mem_mgmt-
> >mem_pool_buf_list);
> 
> [Severity: Critical]
> If priv->mem_pool is NULL, the mem_pool_buf_list remains zero-initialized.
> Will se_cleanup_mem_pool_buf() later unconditionally traverse this
> uninitialized list head via list_for_each_entry_safe() and cause a kernel panic
> when the file descriptor is closed?
> 
> [ ... ]
> 
Accepted.
 -       se_cleanup_mem_pool_buf(dev_ctx, reclaim);
+       if (priv->mem_pool)
+               se_cleanup_mem_pool_buf(dev_ctx, reclaim);

> > +static int se_ioctl_cmd_snd_rcv_rsp_handler(struct se_if_device_ctx
> *dev_ctx,
> > +					    void __user *uarg)
> > +{
> [ ... ]
> > +	struct se_api_msg *tx_msg __free(kfree) =
> > +
> 	memdup_user(u64_to_user_ptr(cmd_snd_rcv_rsp_info.tx_buf),
> > +			    cmd_snd_rcv_rsp_info.tx_buf_sz);
> > +	if (IS_ERR(tx_msg)) {
> > +		err = PTR_ERR(tx_msg);
> > +		se_ioctl_cmd_snd_rcv_cleanup(dev_ctx, uarg,
> &cmd_snd_rcv_rsp_info);
> > +		return err;
> > +	}
> 
> [Severity: Critical]
> Does this allow user-space to construct raw command payloads and embed
> unvalidated DMA addresses? Since these addresses are not validated to
> ensure they point to memory allocated via SE_IOCTL_SETUP_IOBUF before
> calling ele_msg_send(), could a malicious application embed spoofed physical
> addresses (like kernel page tables) and instruct the hardware to access them?
> 
> [ ... ]
Not an issue.
- [Critical, new] Userspace can construct raw command payloads and embed
  unvalidated DMA addresses (confused-deputy): NO-CHANGE. 

Passing physical/DMA addresses inside the message body is inherent to the
firmware ABI this interface exposes, and is constrained by
se_chk_tx_msg_hdr() -> ele_uapi_allowed_base_cmd()/
ele_uapi_allowed_fw_cmd(), which restrict the command set to a
vetted allow-list (power-management, reset, BBSM, RNG init, FW
re-init, CAAM resource release and SE internal memory management are
all blocked). The enclave firmware is the trust boundary that
validates addresses against the caller's provisioned resources;
per-command address bounds-checking in the kernel would duplicate that
policy without owning the authoritative resource map. No code change.

> 
> > +	struct se_api_msg *rx_msg __free(kfree) =
> > +		kzalloc(cmd_snd_rcv_rsp_info.rx_buf_sz, GFP_KERNEL);
> > +	if (!rx_msg) {
> > +		se_ioctl_cmd_snd_rcv_cleanup(dev_ctx, uarg,
> &cmd_snd_rcv_rsp_info);
> > +		return -ENOMEM;
> > +	}
> 
> [Severity: High]
> Can this lead to a slab-out-of-bounds read in se_val_rsp_hdr_n_status()?
> If rx_buf_sz is small (e.g., 4 bytes) but the firmware returns a header size
> larger than SE_MU_HDR_WORD_SZ for debug commands, will
> check_hdr_exception_for_sz() bypass the mismatch check and cause
> se_val_rsp_hdr_n_status() to read past the 4-byte allocated rx_msg buffer?
> 
Accepted. Will be fixed in next re-spin.
-       if (header->size > SE_MU_HDR_WORD_SZ) {
+       if (header->size > SE_MU_HDR_WORD_SZ && (sz >> 2) > SE_MU_HDR_WORD_SZ) {
                status = RES_STATUS(msg->data[0]);
                if (status != priv->if_defs->success_tag) {

> [ ... ]
> 
> > +	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]
> Does this mask the failure when the firmware command fails? If
> se_val_rsp_hdr_n_status() sets a negative error code in rsp_status_err, it skips
> se_dev_ctx_cpy_out_data(), but the positive message size from
> ele_msg_send_rcv() stored in err isn't updated. Could this cause the ioctl to
> return a positive value to userspace, falsely indicating success?
> 
Accepted. 
diff --git a/drivers/firmware/imx/se_ctrl.c b/drivers/firmware/imx/se_ctrl.c
index a00cf75b0d8c..a3a70cf45d80 100644
--- a/drivers/firmware/imx/se_ctrl.c
+++ b/drivers/firmware/imx/se_ctrl.c
@@ -1370,7 +1370,8 @@ static int se_ioctl_cmd_snd_rcv_rsp_handler(struct se_if_device_ctx *dev_ctx,
 
                return err;
        }
-
+       /* updating the actual received buffer size. */
+       cmd_snd_rcv_rsp_info.rx_buf_sz = err;
+       err = 0;
        dev_dbg(priv->dev, "%s: %s %s.", dev_ctx->devname, __func__,
                "message received, start transmit to user");

> --
> Sashiko AI review ยท https://sashiko.dev/#/patchset/20260729-imx-se-if-v31-0-
> [email protected]?part=5