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

Dan Carpenter dan.carpenter at oracle.com
Thu Sep 1 02:33:35 PDT 2022


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