dmaengine: idxd: fix memory leak in error handling path of idxd_setup_wqs
authorShuai Xue <xueshuai@linux.alibaba.com>
Fri, 4 Apr 2025 12:02:09 +0000 (20:02 +0800)
committerVinod Koul <vkoul@kernel.org>
Wed, 14 May 2025 13:40:39 +0000 (14:40 +0100)
Memory allocated for wqs is not freed if an error occurs during
idxd_setup_wqs(). To fix it, free the allocated memory in the reverse
order of allocation before exiting the function in case of an error.

Fixes: 7c5dd23e57c1 ("dmaengine: idxd: fix wq conf_dev 'struct device' lifetime")
Fixes: 700af3a0a26c ("dmaengine: idxd: add 'struct idxd_dev' as wrapper for conf_dev")
Fixes: de5819b99489 ("dmaengine: idxd: track enabled workqueues in bitmap")
Fixes: b0325aefd398 ("dmaengine: idxd: add WQ operation cap restriction support")
Cc: stable@vger.kernel.org
Signed-off-by: Shuai Xue <xueshuai@linux.alibaba.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Fenghua Yu <fenghuay@nvidia.com>
Link: https://lore.kernel.org/r/20250404120217.48772-2-xueshuai@linux.alibaba.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
drivers/dma/idxd/init.c

index fca1d29249998d9238550681feb86162454fd6d7..80fb189c9624f65b204780a227b958447fc89464 100644 (file)
@@ -169,8 +169,8 @@ static int idxd_setup_wqs(struct idxd_device *idxd)
 
        idxd->wq_enable_map = bitmap_zalloc_node(idxd->max_wqs, GFP_KERNEL, dev_to_node(dev));
        if (!idxd->wq_enable_map) {
-               kfree(idxd->wqs);
-               return -ENOMEM;
+               rc = -ENOMEM;
+               goto err_bitmap;
        }
 
        for (i = 0; i < idxd->max_wqs; i++) {
@@ -189,10 +189,8 @@ static int idxd_setup_wqs(struct idxd_device *idxd)
                conf_dev->bus = &dsa_bus_type;
                conf_dev->type = &idxd_wq_device_type;
                rc = dev_set_name(conf_dev, "wq%d.%d", idxd->id, wq->id);
-               if (rc < 0) {
-                       put_device(conf_dev);
+               if (rc < 0)
                        goto err;
-               }
 
                mutex_init(&wq->wq_lock);
                init_waitqueue_head(&wq->err_queue);
@@ -203,7 +201,6 @@ static int idxd_setup_wqs(struct idxd_device *idxd)
                wq->enqcmds_retries = IDXD_ENQCMDS_RETRIES;
                wq->wqcfg = kzalloc_node(idxd->wqcfg_size, GFP_KERNEL, dev_to_node(dev));
                if (!wq->wqcfg) {
-                       put_device(conf_dev);
                        rc = -ENOMEM;
                        goto err;
                }
@@ -211,9 +208,8 @@ static int idxd_setup_wqs(struct idxd_device *idxd)
                if (idxd->hw.wq_cap.op_config) {
                        wq->opcap_bmap = bitmap_zalloc(IDXD_MAX_OPCAP_BITS, GFP_KERNEL);
                        if (!wq->opcap_bmap) {
-                               put_device(conf_dev);
                                rc = -ENOMEM;
-                               goto err;
+                               goto err_opcap_bmap;
                        }
                        bitmap_copy(wq->opcap_bmap, idxd->opcap_bmap, IDXD_MAX_OPCAP_BITS);
                }
@@ -224,12 +220,28 @@ static int idxd_setup_wqs(struct idxd_device *idxd)
 
        return 0;
 
- err:
+err_opcap_bmap:
+       kfree(wq->wqcfg);
+
+err:
+       put_device(conf_dev);
+       kfree(wq);
+
        while (--i >= 0) {
                wq = idxd->wqs[i];
+               if (idxd->hw.wq_cap.op_config)
+                       bitmap_free(wq->opcap_bmap);
+               kfree(wq->wqcfg);
                conf_dev = wq_confdev(wq);
                put_device(conf_dev);
+               kfree(wq);
+
        }
+       bitmap_free(idxd->wq_enable_map);
+
+err_bitmap:
+       kfree(idxd->wqs);
+
        return rc;
 }