All of lore.kernel.org
 help / color / mirror / Atom feed
* [bug report] dmaengine: idxd: fix memory leak in error handling path of idxd_setup_wqs
@ 2025-05-26  9:01 Dan Carpenter
  2025-05-26  9:26 ` Shuai Xue
  0 siblings, 1 reply; 3+ messages in thread
From: Dan Carpenter @ 2025-05-26  9:01 UTC (permalink / raw)
  To: Shuai Xue; +Cc: dmaengine

Hello Shuai Xue,

Commit 3fd2f4bc010c ("dmaengine: idxd: fix memory leak in error
handling path of idxd_setup_wqs") from Apr 4, 2025 (linux-next),
leads to the following Smatch static checker warning:

	drivers/dma/idxd/init.c:246 idxd_setup_wqs()
	error: uninitialized symbol 'conf_dev'.

drivers/dma/idxd/init.c
    177 static int idxd_setup_wqs(struct idxd_device *idxd)
    178 {
    179         struct device *dev = &idxd->pdev->dev;
    180         struct idxd_wq *wq;
    181         struct device *conf_dev;
    182         int i, rc;
    183 
    184         idxd->wqs = kcalloc_node(idxd->max_wqs, sizeof(struct idxd_wq *),
    185                                  GFP_KERNEL, dev_to_node(dev));
    186         if (!idxd->wqs)
    187                 return -ENOMEM;
    188 
    189         idxd->wq_enable_map = bitmap_zalloc_node(idxd->max_wqs, GFP_KERNEL, dev_to_node(dev));
    190         if (!idxd->wq_enable_map) {
    191                 rc = -ENOMEM;
    192                 goto err_bitmap;
    193         }
    194 
    195         for (i = 0; i < idxd->max_wqs; i++) {
    196                 wq = kzalloc_node(sizeof(*wq), GFP_KERNEL, dev_to_node(dev));
    197                 if (!wq) {
    198                         rc = -ENOMEM;
    199                         goto err;

On this error path we either free an uninitialized variable or we
double free conf_dev.

    200                 }
    201 
    202                 idxd_dev_set_type(&wq->idxd_dev, IDXD_DEV_WQ);
    203                 conf_dev = wq_confdev(wq);
    204                 wq->id = i;
    205                 wq->idxd = idxd;
    206                 device_initialize(wq_confdev(wq));
    207                 conf_dev->parent = idxd_confdev(idxd);
    208                 conf_dev->bus = &dsa_bus_type;
    209                 conf_dev->type = &idxd_wq_device_type;
    210                 rc = dev_set_name(conf_dev, "wq%d.%d", idxd->id, wq->id);
    211                 if (rc < 0)
    212                         goto err;

When we're cleaning up loops what I recommend is that we clean up the
partial iterations before the goto.

		if (rc < 0) {
			put_device(conf_dev);
			kfree(wq);
			goto unwind_loop;
		}

That's sort of how the code was written originally but it missed some
frees.


    213 
    214                 mutex_init(&wq->wq_lock);
    215                 init_waitqueue_head(&wq->err_queue);
    216                 init_completion(&wq->wq_dead);
    217                 init_completion(&wq->wq_resurrect);
    218                 wq->max_xfer_bytes = WQ_DEFAULT_MAX_XFER;
    219                 idxd_wq_set_max_batch_size(idxd->data->type, wq, WQ_DEFAULT_MAX_BATCH);
    220                 wq->enqcmds_retries = IDXD_ENQCMDS_RETRIES;
    221                 wq->wqcfg = kzalloc_node(idxd->wqcfg_size, GFP_KERNEL, dev_to_node(dev));
    222                 if (!wq->wqcfg) {
    223                         rc = -ENOMEM;
    224                         goto err;
    225                 }

Same:

		if (!wq->wqcfg) {
			put_device(conf_dev);
			kfree(wq);
			rc = -ENOMEM;
			goto unwind_loop;
		}

    226 
    227                 if (idxd->hw.wq_cap.op_config) {
    228                         wq->opcap_bmap = bitmap_zalloc(IDXD_MAX_OPCAP_BITS, GFP_KERNEL);
    229                         if (!wq->opcap_bmap) {
    230                                 rc = -ENOMEM;
    231                                 goto err_opcap_bmap;
    232                         }

		if (!wq->wqcfg) {
			kfree(wq->wqcfg);
			put_device(conf_dev);
			kfree(wq);
			rc = -ENOMEM;
			goto unwind_loop;
		}


    233                         bitmap_copy(wq->opcap_bmap, idxd->opcap_bmap, IDXD_MAX_OPCAP_BITS);
    234                 }
    235                 mutex_init(&wq->uc_lock);
    236                 xa_init(&wq->upasid_xa);
    237                 idxd->wqs[i] = wq;
    238         }
    239 

Imagine if we add another two allocations here.

		foo = alloc();
		if (!foo)
			goto err;
		bar = alloc();
		if (!bar)
			goto free_foo;


    240         return 0;
    241 

Then we have to do a little bunny hop.

free_foo:
	free(foo);
	goto err; // <-- bunny hop

err_opcap_bmap:
	kfree(wq->wqcfg);

People often get confused and forget the bunny hop.

    242 err_opcap_bmap:
    243         kfree(wq->wqcfg);
    244 
    245 err:
--> 246         put_device(conf_dev);
    247         kfree(wq);
    248 
    249         while (--i >= 0) {
    250                 wq = idxd->wqs[i];
    251                 if (idxd->hw.wq_cap.op_config)
    252                         bitmap_free(wq->opcap_bmap);
    253                 kfree(wq->wqcfg);
    254                 conf_dev = wq_confdev(wq);
    255                 put_device(conf_dev);
    256                 kfree(wq);
    257 
    258         }
    259         bitmap_free(idxd->wq_enable_map);
    260 
    261 err_bitmap:
    262         kfree(idxd->wqs);
    263 
    264         return rc;
    265 }

regards,
dan carpenter

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [bug report] dmaengine: idxd: fix memory leak in error handling path of idxd_setup_wqs
  2025-05-26  9:01 [bug report] dmaengine: idxd: fix memory leak in error handling path of idxd_setup_wqs Dan Carpenter
@ 2025-05-26  9:26 ` Shuai Xue
  2025-05-28  2:22   ` Vinicius Costa Gomes
  0 siblings, 1 reply; 3+ messages in thread
From: Shuai Xue @ 2025-05-26  9:26 UTC (permalink / raw)
  To: Dan Carpenter, Dave Jiang, Colin King (gmail); +Cc: dmaengine



在 2025/5/26 17:01, Dan Carpenter 写道:
> Hello Shuai Xue,
> 
> Commit 3fd2f4bc010c ("dmaengine: idxd: fix memory leak in error
> handling path of idxd_setup_wqs") from Apr 4, 2025 (linux-next),
> leads to the following Smatch static checker warning:
> 
> 	drivers/dma/idxd/init.c:246 idxd_setup_wqs()
> 	error: uninitialized symbol 'conf_dev'.
> 
> drivers/dma/idxd/init.c
>      177 static int idxd_setup_wqs(struct idxd_device *idxd)
>      178 {
>      179         struct device *dev = &idxd->pdev->dev;
>      180         struct idxd_wq *wq;
>      181         struct device *conf_dev;
>      182         int i, rc;
>      183
>      184         idxd->wqs = kcalloc_node(idxd->max_wqs, sizeof(struct idxd_wq *),
>      185                                  GFP_KERNEL, dev_to_node(dev));
>      186         if (!idxd->wqs)
>      187                 return -ENOMEM;
>      188
>      189         idxd->wq_enable_map = bitmap_zalloc_node(idxd->max_wqs, GFP_KERNEL, dev_to_node(dev));
>      190         if (!idxd->wq_enable_map) {
>      191                 rc = -ENOMEM;
>      192                 goto err_bitmap;
>      193         }
>      194
>      195         for (i = 0; i < idxd->max_wqs; i++) {
>      196                 wq = kzalloc_node(sizeof(*wq), GFP_KERNEL, dev_to_node(dev));
>      197                 if (!wq) {
>      198                         rc = -ENOMEM;
>      199                         goto err;
> 
> On this error path we either free an uninitialized variable or we
> double free conf_dev.

Hi, Dan,

Thanks for reporting this bug:)

It has reported by Colin but he forgot to cc mailing list.
And I sent a patch to fix it:
https://lore.kernel.org/lkml/19668a72-c523-42ab-87ac-990a4baac214@intel.com/

> 
>      200                 }
>      201
>      202                 idxd_dev_set_type(&wq->idxd_dev, IDXD_DEV_WQ);
>      203                 conf_dev = wq_confdev(wq);
>      204                 wq->id = i;
>      205                 wq->idxd = idxd;
>      206                 device_initialize(wq_confdev(wq));
>      207                 conf_dev->parent = idxd_confdev(idxd);
>      208                 conf_dev->bus = &dsa_bus_type;
>      209                 conf_dev->type = &idxd_wq_device_type;
>      210                 rc = dev_set_name(conf_dev, "wq%d.%d", idxd->id, wq->id);
>      211                 if (rc < 0)
>      212                         goto err;
> 
> When we're cleaning up loops what I recommend is that we clean up the
> partial iterations before the goto.
> 
> 		if (rc < 0) {
> 			put_device(conf_dev);
> 			kfree(wq);
> 			goto unwind_loop;
> 		}
> 
> That's sort of how the code was written originally but it missed some
> frees.
> 
> 
>      213
>      214                 mutex_init(&wq->wq_lock);
>      215                 init_waitqueue_head(&wq->err_queue);
>      216                 init_completion(&wq->wq_dead);
>      217                 init_completion(&wq->wq_resurrect);
>      218                 wq->max_xfer_bytes = WQ_DEFAULT_MAX_XFER;
>      219                 idxd_wq_set_max_batch_size(idxd->data->type, wq, WQ_DEFAULT_MAX_BATCH);
>      220                 wq->enqcmds_retries = IDXD_ENQCMDS_RETRIES;
>      221                 wq->wqcfg = kzalloc_node(idxd->wqcfg_size, GFP_KERNEL, dev_to_node(dev));
>      222                 if (!wq->wqcfg) {
>      223                         rc = -ENOMEM;
>      224                         goto err;
>      225                 }
> 
> Same:
> 
> 		if (!wq->wqcfg) {
> 			put_device(conf_dev);
> 			kfree(wq);
> 			rc = -ENOMEM;
> 			goto unwind_loop;
> 		}
> 
>      226
>      227                 if (idxd->hw.wq_cap.op_config) {
>      228                         wq->opcap_bmap = bitmap_zalloc(IDXD_MAX_OPCAP_BITS, GFP_KERNEL);
>      229                         if (!wq->opcap_bmap) {
>      230                                 rc = -ENOMEM;
>      231                                 goto err_opcap_bmap;
>      232                         }
> 
> 		if (!wq->wqcfg) {
> 			kfree(wq->wqcfg);
> 			put_device(conf_dev);
> 			kfree(wq);
> 			rc = -ENOMEM;
> 			goto unwind_loop;
> 		}
> 
> 
>      233                         bitmap_copy(wq->opcap_bmap, idxd->opcap_bmap, IDXD_MAX_OPCAP_BITS);
>      234                 }
>      235                 mutex_init(&wq->uc_lock);
>      236                 xa_init(&wq->upasid_xa);
>      237                 idxd->wqs[i] = wq;
>      238         }
>      239
> 
> Imagine if we add another two allocations here.
> 
> 		foo = alloc();
> 		if (!foo)
> 			goto err;
> 		bar = alloc();
> 		if (!bar)
> 			goto free_foo;
> 
> 
>      240         return 0;
>      241
> 
> Then we have to do a little bunny hop.
> 
> free_foo:
> 	free(foo);
> 	goto err; // <-- bunny hop
> 
> err_opcap_bmap:
> 	kfree(wq->wqcfg);
> 
> People often get confused and forget the bunny hop.

I think so, this is the way I used in the original patch I send.
but reviewer Markus point to move common free code to additional
jump targets.

https://lore.kernel.org/lkml/98327a4d-7684-4908-9d67-5dfcaa229ae1@web.de/

I feel free to change back to clean up the partial iterations before the goto.
@Dave, which way do you like?

(Dave is on vacation, I can send a new patch if he prefer the latter way)

> 
>      242 err_opcap_bmap:
>      243         kfree(wq->wqcfg);
>      244
>      245 err:
> --> 246         put_device(conf_dev);
>      247         kfree(wq);
>      248
>      249         while (--i >= 0) {
>      250                 wq = idxd->wqs[i];
>      251                 if (idxd->hw.wq_cap.op_config)
>      252                         bitmap_free(wq->opcap_bmap);
>      253                 kfree(wq->wqcfg);
>      254                 conf_dev = wq_confdev(wq);
>      255                 put_device(conf_dev);
>      256                 kfree(wq);
>      257
>      258         }
>      259         bitmap_free(idxd->wq_enable_map);
>      260
>      261 err_bitmap:
>      262         kfree(idxd->wqs);
>      263
>      264         return rc;
>      265 }
> 
> regards,
> dan carpenter


Thanks.

Regards
Shuai

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [bug report] dmaengine: idxd: fix memory leak in error handling path of idxd_setup_wqs
  2025-05-26  9:26 ` Shuai Xue
@ 2025-05-28  2:22   ` Vinicius Costa Gomes
  0 siblings, 0 replies; 3+ messages in thread
From: Vinicius Costa Gomes @ 2025-05-28  2:22 UTC (permalink / raw)
  To: Shuai Xue, Dan Carpenter, Dave Jiang, Colin King (gmail); +Cc: dmaengine

Hi Shuai,

Shuai Xue <xueshuai@linux.alibaba.com> writes:

> 在 2025/5/26 17:01, Dan Carpenter 写道:
>> Hello Shuai Xue,
>> 
>> Commit 3fd2f4bc010c ("dmaengine: idxd: fix memory leak in error
>> handling path of idxd_setup_wqs") from Apr 4, 2025 (linux-next),
>> leads to the following Smatch static checker warning:
>> 
>> 	drivers/dma/idxd/init.c:246 idxd_setup_wqs()
>> 	error: uninitialized symbol 'conf_dev'.
>> 
>> drivers/dma/idxd/init.c
>>      177 static int idxd_setup_wqs(struct idxd_device *idxd)
>>      178 {
>>      179         struct device *dev = &idxd->pdev->dev;
>>      180         struct idxd_wq *wq;
>>      181         struct device *conf_dev;
>>      182         int i, rc;
>>      183
>>      184         idxd->wqs = kcalloc_node(idxd->max_wqs, sizeof(struct idxd_wq *),
>>      185                                  GFP_KERNEL, dev_to_node(dev));
>>      186         if (!idxd->wqs)
>>      187                 return -ENOMEM;
>>      188
>>      189         idxd->wq_enable_map = bitmap_zalloc_node(idxd->max_wqs, GFP_KERNEL, dev_to_node(dev));
>>      190         if (!idxd->wq_enable_map) {
>>      191                 rc = -ENOMEM;
>>      192                 goto err_bitmap;
>>      193         }
>>      194
>>      195         for (i = 0; i < idxd->max_wqs; i++) {
>>      196                 wq = kzalloc_node(sizeof(*wq), GFP_KERNEL, dev_to_node(dev));
>>      197                 if (!wq) {
>>      198                         rc = -ENOMEM;
>>      199                         goto err;
>> 
>> On this error path we either free an uninitialized variable or we
>> double free conf_dev.
>
> Hi, Dan,
>
> Thanks for reporting this bug:)
>
> It has reported by Colin but he forgot to cc mailing list.
> And I sent a patch to fix it:
> https://lore.kernel.org/lkml/19668a72-c523-42ab-87ac-990a4baac214@intel.com/
>
>> 
>>      200                 }
>>      201
>>      202                 idxd_dev_set_type(&wq->idxd_dev, IDXD_DEV_WQ);
>>      203                 conf_dev = wq_confdev(wq);
>>      204                 wq->id = i;
>>      205                 wq->idxd = idxd;
>>      206                 device_initialize(wq_confdev(wq));
>>      207                 conf_dev->parent = idxd_confdev(idxd);
>>      208                 conf_dev->bus = &dsa_bus_type;
>>      209                 conf_dev->type = &idxd_wq_device_type;
>>      210                 rc = dev_set_name(conf_dev, "wq%d.%d", idxd->id, wq->id);
>>      211                 if (rc < 0)
>>      212                         goto err;
>> 
>> When we're cleaning up loops what I recommend is that we clean up the
>> partial iterations before the goto.
>> 
>> 		if (rc < 0) {
>> 			put_device(conf_dev);
>> 			kfree(wq);
>> 			goto unwind_loop;
>> 		}
>> 
>> That's sort of how the code was written originally but it missed some
>> frees.
>> 
>> 
>>      213
>>      214                 mutex_init(&wq->wq_lock);
>>      215                 init_waitqueue_head(&wq->err_queue);
>>      216                 init_completion(&wq->wq_dead);
>>      217                 init_completion(&wq->wq_resurrect);
>>      218                 wq->max_xfer_bytes = WQ_DEFAULT_MAX_XFER;
>>      219                 idxd_wq_set_max_batch_size(idxd->data->type, wq, WQ_DEFAULT_MAX_BATCH);
>>      220                 wq->enqcmds_retries = IDXD_ENQCMDS_RETRIES;
>>      221                 wq->wqcfg = kzalloc_node(idxd->wqcfg_size, GFP_KERNEL, dev_to_node(dev));
>>      222                 if (!wq->wqcfg) {
>>      223                         rc = -ENOMEM;
>>      224                         goto err;
>>      225                 }
>> 
>> Same:
>> 
>> 		if (!wq->wqcfg) {
>> 			put_device(conf_dev);
>> 			kfree(wq);
>> 			rc = -ENOMEM;
>> 			goto unwind_loop;
>> 		}
>> 
>>      226
>>      227                 if (idxd->hw.wq_cap.op_config) {
>>      228                         wq->opcap_bmap = bitmap_zalloc(IDXD_MAX_OPCAP_BITS, GFP_KERNEL);
>>      229                         if (!wq->opcap_bmap) {
>>      230                                 rc = -ENOMEM;
>>      231                                 goto err_opcap_bmap;
>>      232                         }
>> 
>> 		if (!wq->wqcfg) {
>> 			kfree(wq->wqcfg);
>> 			put_device(conf_dev);
>> 			kfree(wq);
>> 			rc = -ENOMEM;
>> 			goto unwind_loop;
>> 		}
>> 
>> 
>>      233                         bitmap_copy(wq->opcap_bmap, idxd->opcap_bmap, IDXD_MAX_OPCAP_BITS);
>>      234                 }
>>      235                 mutex_init(&wq->uc_lock);
>>      236                 xa_init(&wq->upasid_xa);
>>      237                 idxd->wqs[i] = wq;
>>      238         }
>>      239
>> 
>> Imagine if we add another two allocations here.
>> 
>> 		foo = alloc();
>> 		if (!foo)
>> 			goto err;
>> 		bar = alloc();
>> 		if (!bar)
>> 			goto free_foo;
>> 
>> 
>>      240         return 0;
>>      241
>> 
>> Then we have to do a little bunny hop.
>> 
>> free_foo:
>> 	free(foo);
>> 	goto err; // <-- bunny hop
>> 
>> err_opcap_bmap:
>> 	kfree(wq->wqcfg);
>> 
>> People often get confused and forget the bunny hop.
>
> I think so, this is the way I used in the original patch I send.
> but reviewer Markus point to move common free code to additional
> jump targets.
>
> https://lore.kernel.org/lkml/98327a4d-7684-4908-9d67-5dfcaa229ae1@web.de/
>
> I feel free to change back to clean up the partial iterations before the goto.
> @Dave, which way do you like?
>
> (Dave is on vacation, I can send a new patch if he prefer the latter way)
>

I think the solution you proposed is fine. At least to me, it looks clear
enough and seems to fix the problem.

>> 
>>      242 err_opcap_bmap:
>>      243         kfree(wq->wqcfg);
>>      244
>>      245 err:
>> --> 246         put_device(conf_dev);
>>      247         kfree(wq);
>>      248
>>      249         while (--i >= 0) {
>>      250                 wq = idxd->wqs[i];
>>      251                 if (idxd->hw.wq_cap.op_config)
>>      252                         bitmap_free(wq->opcap_bmap);
>>      253                 kfree(wq->wqcfg);
>>      254                 conf_dev = wq_confdev(wq);
>>      255                 put_device(conf_dev);
>>      256                 kfree(wq);
>>      257
>>      258         }
>>      259         bitmap_free(idxd->wq_enable_map);
>>      260
>>      261 err_bitmap:
>>      262         kfree(idxd->wqs);
>>      263
>>      264         return rc;
>>      265 }
>> 
>> regards,
>> dan carpenter
>
>
> Thanks.
>
> Regards
> Shuai


Cheers,
-- 
Vinicius

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2025-05-28  2:22 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-05-26  9:01 [bug report] dmaengine: idxd: fix memory leak in error handling path of idxd_setup_wqs Dan Carpenter
2025-05-26  9:26 ` Shuai Xue
2025-05-28  2:22   ` Vinicius Costa Gomes

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.