platform/x86/intel/vsec: Fix xa_alloc memory leak
authorDavid E. Box <david.e.box@linux.intel.com>
Wed, 29 Nov 2023 22:21:13 +0000 (14:21 -0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 25 Jan 2024 23:35:14 +0000 (15:35 -0800)
[ Upstream commit 8cbcc1dbf8a62c730fadd60de761e0658547a589 ]

Commit 936874b77dd0 ("platform/x86/intel/vsec: Add PCI error recovery
support to Intel PMT") added an xarray to track the list of vsec devices to
be recovered after a PCI error. But it did not provide cleanup for the list
leading to a memory leak that was caught by kmemleak.  Do xa_alloc() before
devm_add_action_or_reset() so that the list may be cleaned up with
xa_erase() in the release function.

Fixes: 936874b77dd0 ("platform/x86/intel/vsec: Add PCI error recovery support to Intel PMT")
Signed-off-by: David E. Box <david.e.box@linux.intel.com>
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Link: https://lore.kernel.org/r/20231129222132.2331261-2-david.e.box@linux.intel.com
[hdegoede@redhat.com: Add missing xa_erase() on error-exit
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/platform/x86/intel/vsec.c
drivers/platform/x86/intel/vsec.h

index c1f9e44..343ab6a 100644 (file)
@@ -120,6 +120,8 @@ static void intel_vsec_dev_release(struct device *dev)
 {
        struct intel_vsec_device *intel_vsec_dev = dev_to_ivdev(dev);
 
+       xa_erase(&auxdev_array, intel_vsec_dev->id);
+
        mutex_lock(&vsec_ida_lock);
        ida_free(intel_vsec_dev->ida, intel_vsec_dev->auxdev.id);
        mutex_unlock(&vsec_ida_lock);
@@ -135,19 +137,28 @@ int intel_vsec_add_aux(struct pci_dev *pdev, struct device *parent,
        struct auxiliary_device *auxdev = &intel_vsec_dev->auxdev;
        int ret, id;
 
-       mutex_lock(&vsec_ida_lock);
-       ret = ida_alloc(intel_vsec_dev->ida, GFP_KERNEL);
-       mutex_unlock(&vsec_ida_lock);
+       ret = xa_alloc(&auxdev_array, &intel_vsec_dev->id, intel_vsec_dev,
+                      PMT_XA_LIMIT, GFP_KERNEL);
        if (ret < 0) {
                kfree(intel_vsec_dev->resource);
                kfree(intel_vsec_dev);
                return ret;
        }
 
+       mutex_lock(&vsec_ida_lock);
+       id = ida_alloc(intel_vsec_dev->ida, GFP_KERNEL);
+       mutex_unlock(&vsec_ida_lock);
+       if (id < 0) {
+               xa_erase(&auxdev_array, intel_vsec_dev->id);
+               kfree(intel_vsec_dev->resource);
+               kfree(intel_vsec_dev);
+               return id;
+       }
+
        if (!parent)
                parent = &pdev->dev;
 
-       auxdev->id = ret;
+       auxdev->id = id;
        auxdev->name = name;
        auxdev->dev.parent = parent;
        auxdev->dev.release = intel_vsec_dev_release;
@@ -169,12 +180,6 @@ int intel_vsec_add_aux(struct pci_dev *pdev, struct device *parent,
        if (ret < 0)
                return ret;
 
-       /* Add auxdev to list */
-       ret = xa_alloc(&auxdev_array, &id, intel_vsec_dev, PMT_XA_LIMIT,
-                      GFP_KERNEL);
-       if (ret)
-               return ret;
-
        return 0;
 }
 EXPORT_SYMBOL_NS_GPL(intel_vsec_add_aux, INTEL_VSEC);
index 0fd042c..0a6201b 100644 (file)
@@ -45,6 +45,7 @@ struct intel_vsec_device {
        struct ida *ida;
        struct intel_vsec_platform_info *info;
        int num_resources;
+       int id; /* xa */
        void *priv_data;
        size_t priv_data_size;
 };