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

"Pankaj Gupta (OSS)" <[email protected]> Wed, 5 Aug 2026 13:12:34 +0000
Newsgroups dev.linux.lists.sashiko-reviews,dev.linux.lists.imx,org.kernel.vger.linux-devicetree
Message-ID <GV2PR04MB122715C29A0A0583510E83E9595D32@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
> [ ... ]
> > @@ -128,6 +178,26 @@ int ele_msg_rcv(struct se_if_device_ctx *dev_ctx,
> struct se_clbk_handle *se_clbk
> >  		}
> > +
> > +		/*
> > +		 * A positive wait return normally means firmware delivered a
> > +		 * response. During teardown, se_if_probe_cleanup() forces this
> > +		 * wait to return via complete_all() without any real response.
> > +		 * If that happens the firmware may still be executing and could
> > +		 * DMA into the shared buffer later. Treat it as a failed
> > +		 * transaction and arm the circuit breaker so the shared memory
> > +		 * is quarantined (not freed) instead of being reclaimed while
> > +		 * the enclave might still write to it.
> > +		 */
> > +		if (is_rsp_wait_with_timeout && atomic_read(&priv-
> >going_away)) {
> > +			spin_lock_irqsave(&se_clbk_hdl->clbk_rx_lock, flags);
> > +			se_clbk_hdl->rx_msg = NULL;
> > +			se_mark_fw_busy(dev_ctx);
> > +			spin_unlock_irqrestore(&se_clbk_hdl->clbk_rx_lock,
> flags);
> > +			ret = -ENODEV;
> > +			break;
> > +		}
> 
> [Severity: High]
> Does this logic unintentionally intercept legitimate teardown responses?
> 
> During se_if_probe_cleanup(), going_away is set to 1. The teardown path then
> calls cleanup_dev_ctx(), which sends graceful close commands via
> se_close_storage() and se_close_session(). These commands explicitly bypass the
> going_away check in ele_msg_send_rcv() so they can be sent.
> 
> However, when the hardware replies, ele_msg_rcv() checks going_away and
> misidentifies the genuine wakeup as the forced unbind abort, failing the close
> operation and leaking the DMA memory.
> 
Accepted. Will be fixed in V34.

> > 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
> [ ... ]
> > @@ -339,31 +709,116 @@ static void se_if_probe_cleanup(void *plat_dev)
> >  	if (!priv)
> >  		return;
> >
> > +	scoped_guard(mutex, &priv->se_if_cmd_lock) {
> > +		/*
> > +		 * Announce teardown before doing anything else.
> ele_msg_send_rcv()
> > +		 * checks going_away under se_if_cmd_lock and bails out
> instead of
> > +		 * arming a new transaction, and ele_msg_rcv() uses it to tell a
> > +		 * teardown-forced completion apart from a genuine firmware
> response.
> > +		 * This must be set before the complete_all() below so those
> checks
> > +		 * are not dead code.
> > +		 */
> > +		atomic_set(&priv->going_away, 1);
> > +		/*
> > +		 * Wake any ioctl thread blocked in ele_msg_rcv() before
> iterating the
> > +		 * device-context list. That waiter sleeps on this completion
> while
> > +		 * holding its dev_ctx->fops_lock, and cleanup_dev_ctx() below
> also
> > +		 * takes dev_ctx->fops_lock. Completing first lets the waiter
> make
> > +		 * progress and drop fops_lock, avoiding an unbind hang.
> > +		 */
> > +		complete_all(&priv->waiting_rsp_clbk_hdl.done);
> > +	}
> 
> [Severity: Critical]
> Could this lead to a deadlock during unbind?
> 
> When an IOCTL calls ele_msg_send_rcv(), it holds se_if_cmd_lock across the
> blocking wait in ele_msg_rcv(). If the device unbinds concurrently,
> se_if_probe_cleanup() attempts to acquire the exact same se_if_cmd_lock
> before executing complete_all() to wake up the IOCTL thread.
> 
> The unbind thread would block waiting for the lock, while the IOCTL thread
> won't release it until it is woken up.

Accepted. Will be fixed in V34.

> 
> > diff --git a/drivers/firmware/imx/ele_fw_api.c
> > b/drivers/firmware/imx/ele_fw_api.c
> > --- a/drivers/firmware/imx/ele_fw_api.c
> > +++ b/drivers/firmware/imx/ele_fw_api.c
> [ ... ]
> > +void fw_api_specific_ops(struct se_if_device_ctx *dev_ctx, struct
> > +se_api_msg *rx_msg) {
> > +	struct se_msg_hdr *header = &rx_msg->header;
> > +	struct se_if_priv *priv = dev_ctx->priv;
> > +
> > +	switch (header->command) {
> > +	case ELE_SESSION_OPEN_REQ:
> > +		dev_ctx->sess_hdl = rx_msg->data[1];
> > +		break;
> 
> [Severity: High]
> What happens if userspace sends multiple ELE_SESSION_OPEN_REQ or
> ELE_STORAGE_OPEN_REQ ioctls on the same file descriptor?
> 
> The new handle is directly assigned to dev_ctx->sess_hdl, overwriting any
> previous handle without closing it. Does this allow unprivileged userspace
> programs to exhaust firmware resources by leaking previous handles?
> 
Accepted. In ele_uapi_allowed_fw_cmd(), it is checked. if the dev_ctx exists, it will be not allowed giving error as -EEXISTS.

@@ -38,10 +38,19 @@ int ele_uapi_allowed_fw_cmd(struct se_if_device_ctx *dev_ctx, struct se_msg_hdr
                return -EOPNOTSUPP;
 
        switch (header->command) {
+       case ELE_SESSION_OPEN_REQ:
+               /* Might be cleared as part of tear down. */
+               ret = dev_ctx->sess_hdl ? -EEXIST : 0;
+               break;
        case ELE_SESSION_CLOSE_REQ:
                /* Might be cleared as part of tear down. */
                ret = dev_ctx->sess_hdl ? 0 : -ENXIO;
                break;
+       case ELE_STORAGE_OPEN_REQ:
+               /* Might be cleared as part of tear down. */
+               ret = dev_ctx->strg_hdl ? -EEXIST : 0;
+               break;
+       }
        case ELE_STORAGE_CLOSE_REQ:
                /* Might be cleared as part of tear down. */
                ret = dev_ctx->strg_hdl ? 0 : -ENXIO;

> [Severity: High]
> Is it safe to unconditionally read rx_msg->data[1] here?
> 

[Not an issue]
Yes. It is safe.
Before the control reaches here to execute fw_api_specific_ops(), it is checked that
func se_val_rsp_hdr_n_status() must not return error.

static int se_ioctl_cmd_snd_rcv_rsp_handler(struct se_if_device_ctx *dev_ctx,
                                            void __user *uarg)
{
        ....

        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 (!err && !rsp_status_err)
                fw_api_specific_ops(dev_ctx, rx_msg);
        ....
}
> The rx_msg buffer is allocated using kzalloc based on the user-provided
> rx_buf_sz in se_ioctl_cmd_snd_rcv_rsp_handler(). If userspace provides an 8-
> byte buffer size and the firmware responds with a truncated payload, would this
> read past the end of the allocation and leak adjacent kernel memory?
>
[Not an issue] 
Since se_if_rx_callback() already clamps the copy to min(rx_msg_sz, exp_rx_msg_sz) & 
Response is validated with successful response, then only data[1] is read.
what must be safely copied already.

> [ ... ]
> > +int se_close_session(struct se_if_priv *priv, u32 session_hdl) {
> > +	struct se_api_msg *tx_msg __free(kfree) = NULL;
> > +	struct se_api_msg *rx_msg __free(kfree) = NULL;
> > +	int ret;
> > +
> > +	if (!priv || !priv->priv_dev_ctx) {
> > +		ret = -EINVAL;
> > +		goto exit;
> > +	}
> 
> [Severity: High]
> Are there concerns with mixing goto-based error handling and scope-based
> cleanup here?
> 
> The cleanup subsystem guidelines advise against using goto labels (like goto exit)
> in the same function as __free annotations, as it can create confusing ownership
> semantics and lead to double-free or resource leak bugs.
> The same pattern is present in se_close_storage(). Would returning directly be
> preferable?
Accepted. Will fix it in v34

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