irqdomain/treewide: Keep firmware node unconditionally allocated
authorThomas Gleixner <tglx@linutronix.de>
Thu, 9 Jul 2020 09:53:06 +0000 (11:53 +0200)
committerThomas Gleixner <tglx@linutronix.de>
Tue, 14 Jul 2020 15:44:42 +0000 (17:44 +0200)
Quite some non OF/ACPI users of irqdomains allocate firmware nodes of type
IRQCHIP_FWNODE_NAMED or IRQCHIP_FWNODE_NAMED_ID and free them right after
creating the irqdomain. The only purpose of these FW nodes is to convey
name information. When this was introduced the core code did not store the
pointer to the node in the irqdomain. A recent change stored the firmware
node pointer in irqdomain for other reasons and missed to notice that the
usage sites which do the alloc_fwnode/create_domain/free_fwnode sequence
are broken by this. Storing a dangling pointer is dangerous itself, but in
case that the domain is destroyed later on this leads to a double free.

Remove the freeing of the firmware node after creating the irqdomain from
all affected call sites to cure this.

Fixes: 711419e504eb ("irqdomain: Add the missing assignment of domain->fwnode for named fwnode")
Reported-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Acked-by: Bjorn Helgaas <bhelgaas@google.com>
Acked-by: Marc Zyngier <maz@kernel.org>
Cc: stable@vger.kernel.org
Link: https://lkml.kernel.org/r/873661qakd.fsf@nanos.tec.linutronix.de
arch/mips/pci/pci-xtalk-bridge.c
arch/x86/kernel/apic/io_apic.c
arch/x86/kernel/apic/msi.c
arch/x86/kernel/apic/vector.c
arch/x86/platform/uv/uv_irq.c
drivers/iommu/amd/iommu.c
drivers/iommu/hyperv-iommu.c
drivers/iommu/intel/irq_remapping.c
drivers/mfd/ioc3.c
drivers/pci/controller/vmd.c

index 3b2552fb773513eff628a4657af79a0e7eb575d5..5958217861b862e45cc1dfe80bf34c33877f60e0 100644 (file)
@@ -627,9 +627,10 @@ static int bridge_probe(struct platform_device *pdev)
                return -ENOMEM;
        domain = irq_domain_create_hierarchy(parent, 0, 8, fn,
                                             &bridge_domain_ops, NULL);
-       irq_domain_free_fwnode(fn);
-       if (!domain)
+       if (!domain) {
+               irq_domain_free_fwnode(fn);
                return -ENOMEM;
+       }
 
        pci_set_flags(PCI_PROBE_ONLY);
 
index ce61e3e7d39944eef4866a0f50bac28b2a25cf42..81ffcfbfaef2b2da2d7fd4175dad6fe88a534cd7 100644 (file)
@@ -2316,12 +2316,12 @@ static int mp_irqdomain_create(int ioapic)
        ip->irqdomain = irq_domain_create_linear(fn, hwirqs, cfg->ops,
                                                 (void *)(long)ioapic);
 
-       /* Release fw handle if it was allocated above */
-       if (!cfg->dev)
-               irq_domain_free_fwnode(fn);
-
-       if (!ip->irqdomain)
+       if (!ip->irqdomain) {
+               /* Release fw handle if it was allocated above */
+               if (!cfg->dev)
+                       irq_domain_free_fwnode(fn);
                return -ENOMEM;
+       }
 
        ip->irqdomain->parent = parent;
 
index 5cbaca58af950e417618279010645aaf2868086f..c2b2911feeefebe32e56a79cf331c9e952667f90 100644 (file)
@@ -263,12 +263,13 @@ void __init arch_init_msi_domain(struct irq_domain *parent)
                msi_default_domain =
                        pci_msi_create_irq_domain(fn, &pci_msi_domain_info,
                                                  parent);
-               irq_domain_free_fwnode(fn);
        }
-       if (!msi_default_domain)
+       if (!msi_default_domain) {
+               irq_domain_free_fwnode(fn);
                pr_warn("failed to initialize irqdomain for MSI/MSI-x.\n");
-       else
+       } else {
                msi_default_domain->flags |= IRQ_DOMAIN_MSI_NOMASK_QUIRK;
+       }
 }
 
 #ifdef CONFIG_IRQ_REMAP
@@ -301,7 +302,8 @@ struct irq_domain *arch_create_remap_msi_irq_domain(struct irq_domain *parent,
        if (!fn)
                return NULL;
        d = pci_msi_create_irq_domain(fn, &pci_msi_ir_domain_info, parent);
-       irq_domain_free_fwnode(fn);
+       if (!d)
+               irq_domain_free_fwnode(fn);
        return d;
 }
 #endif
@@ -364,7 +366,8 @@ static struct irq_domain *dmar_get_irq_domain(void)
        if (fn) {
                dmar_domain = msi_create_irq_domain(fn, &dmar_msi_domain_info,
                                                    x86_vector_domain);
-               irq_domain_free_fwnode(fn);
+               if (!dmar_domain)
+                       irq_domain_free_fwnode(fn);
        }
 out:
        mutex_unlock(&dmar_lock);
@@ -489,7 +492,10 @@ struct irq_domain *hpet_create_irq_domain(int hpet_id)
        }
 
        d = msi_create_irq_domain(fn, domain_info, parent);
-       irq_domain_free_fwnode(fn);
+       if (!d) {
+               irq_domain_free_fwnode(fn);
+               kfree(domain_info);
+       }
        return d;
 }
 
index c48be6e1f6764ca8488f42a112c9ce58ed2c043a..cc8b16f89dd40b721788c8af0e253c490f8e9b3d 100644 (file)
@@ -709,7 +709,6 @@ int __init arch_early_irq_init(void)
        x86_vector_domain = irq_domain_create_tree(fn, &x86_vector_domain_ops,
                                                   NULL);
        BUG_ON(x86_vector_domain == NULL);
-       irq_domain_free_fwnode(fn);
        irq_set_default_host(x86_vector_domain);
 
        arch_init_msi_domain(x86_vector_domain);
index fc13cbbb2dce21f625212a1d70240f84cb0ec0dc..abb6075397f0510b5b6e5b70f322086c8873cba7 100644 (file)
@@ -167,9 +167,10 @@ static struct irq_domain *uv_get_irq_domain(void)
                goto out;
 
        uv_domain = irq_domain_create_tree(fn, &uv_domain_ops, NULL);
-       irq_domain_free_fwnode(fn);
        if (uv_domain)
                uv_domain->parent = x86_vector_domain;
+       else
+               irq_domain_free_fwnode(fn);
 out:
        mutex_unlock(&uv_lock);
 
index 74cca17571725d47cb00923c2fb391de7b9c7d2e..2f22326ee4dfe87887c2b2ed63b9f8a8245d76c5 100644 (file)
@@ -3985,9 +3985,10 @@ int amd_iommu_create_irq_domain(struct amd_iommu *iommu)
        if (!fn)
                return -ENOMEM;
        iommu->ir_domain = irq_domain_create_tree(fn, &amd_ir_domain_ops, iommu);
-       irq_domain_free_fwnode(fn);
-       if (!iommu->ir_domain)
+       if (!iommu->ir_domain) {
+               irq_domain_free_fwnode(fn);
                return -ENOMEM;
+       }
 
        iommu->ir_domain->parent = arch_get_ir_parent_domain();
        iommu->msi_domain = arch_create_remap_msi_irq_domain(iommu->ir_domain,
index 3c0c67a99c7b6407a589d927af15dd4be74e4a02..8919c1c70b68ac8b6007604c689fb3ba746b073f 100644 (file)
@@ -155,7 +155,10 @@ static int __init hyperv_prepare_irq_remapping(void)
                                0, IOAPIC_REMAPPING_ENTRY, fn,
                                &hyperv_ir_domain_ops, NULL);
 
-       irq_domain_free_fwnode(fn);
+       if (!ioapic_ir_domain) {
+               irq_domain_free_fwnode(fn);
+               return -ENOMEM;
+       }
 
        /*
         * Hyper-V doesn't provide irq remapping function for
index 7f87698008150298b1e197c3304ce0b23f966a56..9564d23d094f05d36ee349bff704b31cce5af3e4 100644 (file)
@@ -563,8 +563,8 @@ static int intel_setup_irq_remapping(struct intel_iommu *iommu)
                                            0, INTR_REMAP_TABLE_ENTRIES,
                                            fn, &intel_ir_domain_ops,
                                            iommu);
-       irq_domain_free_fwnode(fn);
        if (!iommu->ir_domain) {
+               irq_domain_free_fwnode(fn);
                pr_err("IR%d: failed to allocate irqdomain\n", iommu->seq_id);
                goto out_free_bitmap;
        }
index 02998d4eb74b037b40f3dad5158b0dbe5ce5ae9c..74cee7cb0afc994306cf4462d7ed289121b80c1e 100644 (file)
@@ -142,10 +142,11 @@ static int ioc3_irq_domain_setup(struct ioc3_priv_data *ipd, int irq)
                goto err;
 
        domain = irq_domain_create_linear(fn, 24, &ioc3_irq_domain_ops, ipd);
-       if (!domain)
+       if (!domain) {
+               irq_domain_free_fwnode(fn);
                goto err;
+       }
 
-       irq_domain_free_fwnode(fn);
        ipd->domain = domain;
 
        irq_set_chained_handler_and_data(irq, ioc3_irq_handler, domain);
index e386d4eac4070dc241ac3a10cc183687d2ce2126..9a64cf90c291b13d1d9cc4c548c131a11885df52 100644 (file)
@@ -546,9 +546,10 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
 
        vmd->irq_domain = pci_msi_create_irq_domain(fn, &vmd_msi_domain_info,
                                                    x86_vector_domain);
-       irq_domain_free_fwnode(fn);
-       if (!vmd->irq_domain)
+       if (!vmd->irq_domain) {
+               irq_domain_free_fwnode(fn);
                return -ENODEV;
+       }
 
        pci_add_resource(&resources, &vmd->resources[0]);
        pci_add_resource_offset(&resources, &vmd->resources[1], offset[0]);