[bug report] media: platform: mtk-mdp3: add MediaTek MDP3 driver

moudy ho moudy.ho at mediatek.com
Thu Sep 1 23:38:46 PDT 2022


Hi Dan,

Thanks for your time and all advices on mdp3 driver!

Regarding note 2 you listed, I tried using a queue event to notify that
the suspend process will not execute until the CMQ send task done.
ref: function "mdp_suspend" in mtk-mdp3-core.cmdq

Although I've added a patch (as below)to fix the smatch bugs and
warnings you mentioned, , and it is less complete than your fix
Message ID = 20220831102853.6843-1-moudy.ho at mediatek.com/

I'd like to confirm whether I should send the v3 patch as per your
suggestion or just drop it and let you deal with it.

Regards,
Moudy

On Thu, 2022-09-01 at 12:33 +0300, Dan Carpenter wrote:
> Hello Moudy Ho,
> 
> The patch 61890ccaefaf: "media: platform: mtk-mdp3: add MediaTek MDP3
> driver" from Aug 23, 2022, leads to the following Smatch static
> checker warning:
> 
> 	drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c:292
> mdp_probe()
> 	error: we previously assumed 'mdp' could be null (see line 188)
> 
> My blog entry gives a good overview of how to write Free the Last
> Thing
> Style error handling.  Let's take a look at it as it applies to
> mdp_probe().
> 
> drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c
>     180 static int mdp_probe(struct platform_device *pdev)
>     181 {
>     182         struct device *dev = &pdev->dev;
>     183         struct mdp_dev *mdp;
>     184         struct platform_device *mm_pdev;
>     185         int ret, i;
>     186 
>     187         mdp = kzalloc(sizeof(*mdp), GFP_KERNEL);
>     188         if (!mdp) {
>     189                 ret = -ENOMEM;
>     190                 goto err_return;
> 
> There is no Last Thing to free.  This should be "return -ENOMEM;".
> This goto will crash.  The Last thing is now "mdp".
> 
>     191         }
>     192 
>     193         mdp->pdev = pdev;
>     194         mdp->mdp_data = of_device_get_match_data(&pdev->dev);
>     195 
>     196         mm_pdev = __get_pdev_by_id(pdev, MDP_INFRA_MMSYS);
>     197         if (!mm_pdev) {
>     198                 ret = -ENODEV;
>     199                 goto err_return;
> 
> This should be "goto err_free_mdp;".  This goto will trigger a series
> of WARN_ON() stack traces.  The Last Thing is now mdp->mdp_mmsys.
> 
>     200         }
>     201         mdp->mdp_mmsys = &mm_pdev->dev;
>     202 
>     203         mm_pdev = __get_pdev_by_id(pdev, MDP_INFRA_MUTEX);
>     204         if (WARN_ON(!mm_pdev)) {
>     205                 ret = -ENODEV;
>     206                 goto err_return;
> 
> This goto should call put on mdp->mdp_mmsys dev_something.  It
> instead
> leaks that.  But it does call mtk_mutex_put() and each call will
> leads
> to a series of stack traces.  The Last Thing is now mm_pdev.  That
> doesn't seem to be stored anywhere so it's going to be complicated to
> free it...  :/
> 
>     207         }
>     208         for (i = 0; i < MDP_PIPE_MAX; i++) {
>     209                 mdp->mdp_mutex[i] = mtk_mutex_get(&mm_pdev-
> >dev);
>     210                 if (!mdp->mdp_mutex[i]) {
>     211                         ret = -ENODEV;
>     212                         goto err_return;
> 
> If this fails it should unwind the successful allocations:
> 
> 	while (--i >= 0) {
> 
> But instead it does unwinds everything.  Stack traces.
> 
>     213                 }
>     214         }
>     215 
>     216         ret = mdp_comp_config(mdp);
>     217         if (ret) {
>     218                 dev_err(dev, "Failed to config mdp
> components\n");
>     219                 goto err_return;
> 
> Alright.  Good.  This will leak the "mm_pdev" references but it will
> not
> print any stack traces.  The mdp_comp_config() function uses One
> Magic
> Free Function style error handling so it is buggy.  The Last thing is
> now comp_config.
> 
>     220         }
>     221 
>     222         mdp->job_wq = alloc_workqueue(MDP_MODULE_NAME,
> WQ_FREEZABLE, 0);
>     223         if (!mdp->job_wq) {
>     224                 dev_err(dev, "Unable to create job
> workqueue\n");
>     225                 ret = -ENOMEM;
>     226                 goto err_deinit_comp;
> 
> Hooray!  This label has a good name and frees the Last Thing.  The
> new
> last thing is the ->job_wq.
> 
>     227         }
>     228 
>     229         mdp->clock_wq = alloc_workqueue(MDP_MODULE_NAME "-
> clock", WQ_FREEZABLE,
>     230                                         0);
>     231         if (!mdp->clock_wq) {
>     232                 dev_err(dev, "Unable to create clock
> workqueue\n");
>     233                 ret = -ENOMEM;
>     234                 goto err_destroy_job_wq;
> 
> Hooray!
> 
>     235         }
>     236 
>     237         mm_pdev = __get_pdev_by_id(pdev, MDP_INFRA_SCP);
>     238         if (WARN_ON(!mm_pdev)) {
>     239                 dev_err(&pdev->dev, "Could not get scp
> device\n");
>     240                 ret = -ENODEV;
>     241                 goto err_destroy_clock_wq;
> 
> Hooray!
> 
>     242         }
>     243         mdp->scp = platform_get_drvdata(mm_pdev);
>     244         mdp->rproc_handle = scp_get_rproc(mdp->scp);
>     245         dev_dbg(&pdev->dev, "MDP rproc_handle: %pK", mdp-
> >rproc_handle);
>     246 
>     247         mutex_init(&mdp->vpu_lock);
>     248         mutex_init(&mdp->m2m_lock);
>     249 
>     250         mdp->cmdq_clt = cmdq_mbox_create(dev, 0);
>     251         if (IS_ERR(mdp->cmdq_clt)) {
>     252                 ret = PTR_ERR(mdp->cmdq_clt);
>     253                 goto err_put_scp;
> 
> Ugh...  The mm_pdev name changed to scp.  It took a while to figure
> that
> out.  This unwinds the __get_pdev_by_id().  Fair enough.  The Last
> thing
> is now cmdq_clt.
> 
>     254         }
>     255 
>     256         init_waitqueue_head(&mdp->callback_wq);
>     257         ida_init(&mdp->mdp_ida);
>     258         platform_set_drvdata(pdev, mdp);
>     259 
>     260         vb2_dma_contig_set_max_seg_size(&pdev->dev,
> DMA_BIT_MASK(32));
>     261 
>     262         ret = v4l2_device_register(dev, &mdp->v4l2_dev);
>     263         if (ret) {
>     264                 dev_err(dev, "Failed to register v4l2
> device\n");
>     265                 ret = -EINVAL;
>     266                 goto err_mbox_destroy;
> 
> The cmdq_clt is an mbox.  Fair enough.  Good name, label does what we
> expected.
> 
>     267         }
>     268 
>     269         ret = mdp_m2m_device_register(mdp);
>     270         if (ret) {
>     271                 v4l2_err(&mdp->v4l2_dev, "Failed to register
> m2m device\n");
>     272                 goto err_unregister_device;
> 
> Good.
> 
>     273         }
>     274 
>     275         dev_dbg(dev, "mdp-%d registered successfully\n",
> pdev->id);
>     276         return 0;
> 
> Summary:
> BUG1: NULL dereference
> BUG2: Stack traces calling mtk_mutex_put().
> BUG3&4: Two __get_pdev_by_id() which need a matching put (reference
> leaks).
> 
>     277 
>     278 err_unregister_device:
>     279         v4l2_device_unregister(&mdp->v4l2_dev);
>     280 err_mbox_destroy:
>     281         cmdq_mbox_destroy(mdp->cmdq_clt);
>     282 err_put_scp:
>     283         scp_put(mdp->scp);
>     284 err_destroy_clock_wq:
>     285         destroy_workqueue(mdp->clock_wq);
>     286 err_destroy_job_wq:
>     287         destroy_workqueue(mdp->job_wq);
>     288 err_deinit_comp:
>     289         mdp_comp_destroy(mdp);
>     290 err_return:
>     291         for (i = 0; i < MDP_PIPE_MAX; i++)
> --> 292                 mtk_mutex_put(mdp->mdp_mutex[i]);
>     293         kfree(mdp);
>     294         dev_dbg(dev, "Errno %d\n", ret);
>     295         return ret;
>     296 }
> 
> regards,
> dan carpenter




More information about the Linux-mediatek mailing list