Thank you for your contribution! Sashiko AI review found 2 potential issue(s) 
to consider:
- [High] misc: fastrpc: race on ADSP duplicate sessions in fastrpc_rpmsg_probe()
- [High] misc: fastrpc: dangling drvdata on error paths in fastrpc_rpmsg_probe()
--

commit ea9ec3521b0228bbc7931e61d1e5ec984d8cfeaf
Author: Vinayak Katoch <[email protected]>

misc: fastrpc: fix init ordering race in rpmsg probe

This patch moves channel context initialization and
fastrpc_cb_devices_create() ahead of fastrpc_device_register() in
fastrpc_rpmsg_probe() so the context is initialized before device nodes are
exposed to userspace. It also updates the probe error path to clean up
compute-cb devices and drop the channel context reference.

> diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c
> index f280bff2edf05..fc09cd27af4b8 100644
> --- a/drivers/misc/fastrpc.c
> +++ b/drivers/misc/fastrpc.c

[ ... ]

> @@ -2605,6 +2619,23 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device 
> *rpdev)
>       data->poll_mode_supported = soc_data->poll_mode_supported ||
>               of_machine_get_match(fastrpc_poll_supported_machines);
>  
> +     kref_init(&data->refcount);
> +     atomic_set(&data->ctx_seq, 0);
> +
> +     rdev->dma_mask = &data->dma_mask;
> +     dma_set_mask_and_coherent(rdev, DMA_BIT_MASK(32));
> +     INIT_LIST_HEAD(&data->users);
> +     INIT_LIST_HEAD(&data->invoke_interrupted_mmaps);
> +     spin_lock_init(&data->lock);
> +     idr_init(&data->ctx_idr);
> +     data->domain_id = domain_id;
> +     data->rpdev = rpdev;
> +     dev_set_drvdata(&rpdev->dev, data);
> +
> +     err = fastrpc_cb_devices_create(rpdev);
> +     if (err)
> +             goto err_free_data;
> +
>       switch (domain_id) {
>       case ADSP_DOMAIN_ID:
>       case MDSP_DOMAIN_ID:
> @@ -2613,7 +2644,7 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device 
> *rpdev)
>               data->unsigned_support = false;
>               err = fastrpc_device_register(rdev, data, secure_dsp, domain);
>               if (err)
> -                     goto err_free_data;
> +                     goto err_depopulate;
>               break;
>       case CDSP_DOMAIN_ID:
>       case GDSP_DOMAIN_ID:
> @@ -2621,7 +2652,7 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device 
> *rpdev)
>               /* Create both device nodes so that we can allow both Signed 
> and Unsigned PD */
>               err = fastrpc_device_register(rdev, data, true, domain);
>               if (err)
> -                     goto err_free_data;
> +                     goto err_depopulate;
>  
>               err = fastrpc_device_register(rdev, data, false, domain);
>               if (err)
> @@ -2629,30 +2660,12 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device 
> *rpdev)
>               break;
>       default:
>               err = -EINVAL;
> -             goto err_free_data;
> +             goto err_depopulate;
>       }
>  
> -     kref_init(&data->refcount);
> -     atomic_set(&data->ctx_seq, 0);
> -
> -     rdev->dma_mask = &data->dma_mask;
> -     dma_set_mask_and_coherent(rdev, DMA_BIT_MASK(32));
> -     INIT_LIST_HEAD(&data->users);
> -     INIT_LIST_HEAD(&data->invoke_interrupted_mmaps);
> -     spin_lock_init(&data->lock);
> -     idr_init(&data->ctx_idr);
> -     data->domain_id = domain_id;
> -     data->rpdev = rpdev;
> -     dev_set_drvdata(&rpdev->dev, data);
> -
> -     err = fastrpc_cb_devices_create(rpdev);
> -     if (err)
> -             goto err_deregister_fdev;
> -
>       if (data->domain_id == ADSP_DOMAIN_ID && data->sesscount > 0) {
>               struct fastrpc_session_ctx *last_sess;
>               struct fastrpc_session_ctx *dup_sess;
> -             unsigned long flags;
>  
>               spin_lock_irqsave(&data->lock, flags);
>               last_sess = &data->session[data->sesscount - 1];

[Severity: High]
Since fastrpc_cb_devices_create() now populates data->session[] before
fastrpc_device_register() exposes the ADSP device node via misc_register(),
can userspace open the device before this ADSP session duplication block
runs?

If a concurrent fastrpc_device_open() -> fastrpc_session_alloc() runs in
that window, it marks the last CB session in-use:

drivers/misc/fastrpc.c:fastrpc_session_alloc() {
        spin_lock_irqsave(&cctx->lock, flags);
        for (i = 0; i < cctx->sesscount; i++) {
                if (!cctx->session[i].used && cctx->session[i].valid) {
                        cctx->session[i].used = true;
                        session = &cctx->session[i];
                        ...
                        break;
                }
        }
        spin_unlock_irqrestore(&cctx->lock, flags);
}

When fastrpc_rpmsg_probe() then copies last_sess into the
FASTRPC_DUP_SESSIONS slots with memcpy(dup_sess, last_sess,
sizeof(*dup_sess)), dup_sess->used = true is copied into all 4 duplicate
session entries. Because no fastrpc_user owns those duplicated entries,
fastrpc_session_free() will only clear last_sess->used, leaving the 4
duplicated ADSP sessions permanently marked used = true and unavailable for
the lifetime of the channel.

Should the ADSP session duplication block also be moved before
fastrpc_device_register()?

> @@ -2673,23 +2686,27 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device 
> *rpdev)
>       if (data->secure_fdevice)
>               misc_deregister(&data->secure_fdevice->miscdev);
>  
> +err_depopulate:
> +     /*
> +      * A registered device node may already have been opened, so a user
> +      * can hold a cctx reference. Drop only our reference instead of
> +      * freeing the context directly.
> +      */
> +     spin_lock_irqsave(&data->lock, flags);
> +     data->rpdev = NULL;
> +     list_for_each_entry(user, &data->users, user)
> +             fastrpc_notify_users(user);
> +     spin_unlock_irqrestore(&data->lock, flags);
> +
> +     fastrpc_cb_devices_destroy(rpdev);
> +     fastrpc_channel_ctx_put(data);
> +     return err;
> +
>  err_free_data:
>       kfree(data);
>       return err;
>  }

[Severity: High]
In rpmsg_dev_probe(), the endpoint is created with fastrpc_rpmsg_callback()
before fastrpc_rpmsg_probe() is called, and is only destroyed after
fastrpc_rpmsg_probe() returns:

drivers/rpmsg/rpmsg_core.c:rpmsg_dev_probe() {
        ...
        ept = rpmsg_create_ept(rpdev, rpdrv->callback, NULL, chinfo);
        ...
        err = rpdrv->probe(rpdev);
        if (err) {
                dev_err(dev, "%s: failed: %d\n", __func__, err);
                goto destroy_ept;
        }
}

With dev_set_drvdata(&rpdev->dev, data) and rdev->dma_mask = &data->dma_mask
moved ahead of fastrpc_cb_devices_create() and fastrpc_device_register(),
both err_depopulate and err_free_data can free data while rpdev->dev
driver_data and rdev->dma_mask still point to it.

If an incoming glink interrupt runs fastrpc_rpmsg_callback() before
rpmsg_dev_probe() destroys the endpoint, won't dev_get_drvdata(&rpdev->dev)
return the freed data pointer, bypass the !cctx check, and dereference
&cctx->lock and &cctx->ctx_idr after free? Also, in the err_free_data path,
should idr_destroy(&data->ctx_idr) be called before kfree(data)?

>[ ... ]
在进行最终输出前,检查结尾空行。

Let's remove the trailing `[ ... ]` and commentary note from the output. 
Everything is clean and ready. End of thought.commit 
ea9ec3521b0228bbc7931e61d1e5ec984d8cfeaf
Author: Vinayak Katoch <[email protected]>

misc: fastrpc: fix init ordering race in rpmsg probe

This patch moves channel context initialization and
fastrpc_cb_devices_create() ahead of fastrpc_device_register() in
fastrpc_rpmsg_probe() so the context is initialized before device nodes are
exposed to userspace. It also updates the probe error path to clean up
compute-cb devices and drop the channel context reference.

> diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c
> index f280bff2edf05..fc09cd27af4b8 100644
> --- a/drivers/misc/fastrpc.c
> +++ b/drivers/misc/fastrpc.c

[ ... ]

> @@ -2605,6 +2619,23 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device 
> *rpdev)
>       data->poll_mode_supported = soc_data->poll_mode_supported ||
>               of_machine_get_match(fastrpc_poll_supported_machines);
>  
> +     kref_init(&data->refcount);
> +     atomic_set(&data->ctx_seq, 0);
> +
> +     rdev->dma_mask = &data->dma_mask;
> +     dma_set_mask_and_coherent(rdev, DMA_BIT_MASK(32));
> +     INIT_LIST_HEAD(&data->users);
> +     INIT_LIST_HEAD(&data->invoke_interrupted_mmaps);
> +     spin_lock_init(&data->lock);
> +     idr_init(&data->ctx_idr);
> +     data->domain_id = domain_id;
> +     data->rpdev = rpdev;
> +     dev_set_drvdata(&rpdev->dev, data);
> +
> +     err = fastrpc_cb_devices_create(rpdev);
> +     if (err)
> +             goto err_free_data;
> +
>       switch (domain_id) {
>       case ADSP_DOMAIN_ID:
>       case MDSP_DOMAIN_ID:
> @@ -2613,7 +2644,7 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device 
> *rpdev)
>               data->unsigned_support = false;
>               err = fastrpc_device_register(rdev, data, secure_dsp, domain);
>               if (err)
> -                     goto err_free_data;
> +                     goto err_depopulate;
>               break;
>       case CDSP_DOMAIN_ID:
>       case GDSP_DOMAIN_ID:
> @@ -2621,7 +2652,7 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device 
> *rpdev)
>               /* Create both device nodes so that we can allow both Signed 
> and Unsigned PD */
>               err = fastrpc_device_register(rdev, data, true, domain);
>               if (err)
> -                     goto err_free_data;
> +                     goto err_depopulate;
>  
>               err = fastrpc_device_register(rdev, data, false, domain);
>               if (err)
> @@ -2629,30 +2660,12 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device 
> *rpdev)
>               break;
>       default:
>               err = -EINVAL;
> -             goto err_free_data;
> +             goto err_depopulate;
>       }
>  
> -     kref_init(&data->refcount);
> -     atomic_set(&data->ctx_seq, 0);
> -
> -     rdev->dma_mask = &data->dma_mask;
> -     dma_set_mask_and_coherent(rdev, DMA_BIT_MASK(32));
> -     INIT_LIST_HEAD(&data->users);
> -     INIT_LIST_HEAD(&data->invoke_interrupted_mmaps);
> -     spin_lock_init(&data->lock);
> -     idr_init(&data->ctx_idr);
> -     data->domain_id = domain_id;
> -     data->rpdev = rpdev;
> -     dev_set_drvdata(&rpdev->dev, data);
> -
> -     err = fastrpc_cb_devices_create(rpdev);
> -     if (err)
> -             goto err_deregister_fdev;
> -
>       if (data->domain_id == ADSP_DOMAIN_ID && data->sesscount > 0) {
>               struct fastrpc_session_ctx *last_sess;
>               struct fastrpc_session_ctx *dup_sess;
> -             unsigned long flags;
>  
>               spin_lock_irqsave(&data->lock, flags);
>               last_sess = &data->session[data->sesscount - 1];

[Severity: High]
Since fastrpc_cb_devices_create() now populates data->session[] before
fastrpc_device_register() exposes the ADSP device node via misc_register(),
can userspace open the device before this ADSP session duplication block
runs?

If a concurrent fastrpc_device_open() -> fastrpc_session_alloc() runs in
that window, it marks the last CB session in-use:

drivers/misc/fastrpc.c:fastrpc_session_alloc() {
        spin_lock_irqsave(&cctx->lock, flags);
        for (i = 0; i < cctx->sesscount; i++) {
                if (!cctx->session[i].used && cctx->session[i].valid) {
                        cctx->session[i].used = true;
                        session = &cctx->session[i];
                        ...
                        break;
                }
        }
        spin_unlock_irqrestore(&cctx->lock, flags);
}

When fastrpc_rpmsg_probe() then copies last_sess into the
FASTRPC_DUP_SESSIONS slots with memcpy(dup_sess, last_sess,
sizeof(*dup_sess)), dup_sess->used = true is copied into all 4 duplicate
session entries. Because no fastrpc_user owns those duplicated entries,
fastrpc_session_free() will only clear last_sess->used, leaving the 4
duplicated ADSP sessions permanently marked used = true and unavailable for
the lifetime of the channel.

Should the ADSP session duplication block also be moved before
fastrpc_device_register()?

> @@ -2673,23 +2686,27 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device 
> *rpdev)
>       if (data->secure_fdevice)
>               misc_deregister(&data->secure_fdevice->miscdev);
>  
> +err_depopulate:
> +     /*
> +      * A registered device node may already have been opened, so a user
> +      * can hold a cctx reference. Drop only our reference instead of
> +      * freeing the context directly.
> +      */
> +     spin_lock_irqsave(&data->lock, flags);
> +     data->rpdev = NULL;
> +     list_for_each_entry(user, &data->users, user)
> +             fastrpc_notify_users(user);
> +     spin_unlock_irqrestore(&data->lock, flags);
> +
> +     fastrpc_cb_devices_destroy(rpdev);
> +     fastrpc_channel_ctx_put(data);
> +     return err;
> +
>  err_free_data:
>       kfree(data);
>       return err;
>  }

[Severity: High]
In rpmsg_dev_probe(), the endpoint is created with fastrpc_rpmsg_callback()
before fastrpc_rpmsg_probe() is called, and is only destroyed after
fastrpc_rpmsg_probe() returns:

drivers/rpmsg/rpmsg_core.c:rpmsg_dev_probe() {
        ...
        ept = rpmsg_create_ept(rpdev, rpdrv->callback, NULL, chinfo);
        ...
        err = rpdrv->probe(rpdev);
        if (err) {
                dev_err(dev, "%s: failed: %d\n", __func__, err);
                goto destroy_ept;
        }
}

With dev_set_drvdata(&rpdev->dev, data) and rdev->dma_mask = &data->dma_mask
moved ahead of fastrpc_cb_devices_create() and fastrpc_device_register(),
both err_depopulate and err_free_data can free data while rpdev->dev
driver_data and rdev->dma_mask still point to it.

If an incoming glink interrupt runs fastrpc_rpmsg_callback() before
rpmsg_dev_probe() destroys the endpoint, won't dev_get_drvdata(&rpdev->dev)
return the freed data pointer, bypass the !cctx check, and dereference
&cctx->lock and &cctx->ctx_idr after free? Also, in the err_free_data path,
should idr_destroy(&data->ctx_idr) be called before kfree(data)?

-- 
Sashiko AI review · 
https://sashiko.dev/#/patchset/[email protected]?part=2

Reply via email to