Commit 33efa29e authored by Lizhi Hou's avatar Lizhi Hou Committed by Bjorn Helgaas
Browse files

PCI: of_property: Handle interrupt parsing failures

of_pci_prop_intr_map() uses uninitialized addr_sz[] values if
of_irq_parse_raw() fails, which leads to intermittent crashes.

Clear addr_sz[] before use so we never use uninitialized elements.

If no valid IRQs are parsed, don't bother adding the interrupt-map
property.

Fixes: 407d1a51 ("PCI: Create device tree node for bridge")
Link: https://lore.kernel.org/r/1696007448-42127-1-git-send-email-lizhi.hou@amd.com


Reported-by: default avatarJonathan Cameron <Jonathan.Cameron@Huawei.com>
Closes: https://lore.kernel.org/all/20230911154856.000076c3@Huawei.com/


Reported-by: default avatarHerve Codina <herve.codina@bootlin.com>
Closes: https://lore.kernel.org/all/20230911171319.495bb837@bootlin.com/


Signed-off-by: default avatarLizhi Hou <lizhi.hou@amd.com>
[bhelgaas: commit log, add similar report from Herve]
Signed-off-by: default avatarBjorn Helgaas <bhelgaas@google.com>
Reviewed-by: default avatarHerve Codina <herve.codina@bootlin.com>
parent 6a878a54
Loading
Loading
Loading
Loading
+18 −7
Original line number Diff line number Diff line
@@ -186,8 +186,8 @@ static int of_pci_prop_interrupts(struct pci_dev *pdev,
static int of_pci_prop_intr_map(struct pci_dev *pdev, struct of_changeset *ocs,
				struct device_node *np)
{
	u32 i, addr_sz[OF_PCI_MAX_INT_PIN] = { 0 }, map_sz = 0;
	struct of_phandle_args out_irq[OF_PCI_MAX_INT_PIN];
	u32 i, addr_sz[OF_PCI_MAX_INT_PIN], map_sz = 0;
	__be32 laddr[OF_PCI_ADDRESS_CELLS] = { 0 };
	u32 int_map_mask[] = { 0xffff00, 0, 0, 7 };
	struct device_node *pnode;
@@ -213,33 +213,44 @@ static int of_pci_prop_intr_map(struct pci_dev *pdev, struct of_changeset *ocs,
		out_irq[i].args[0] = pin;
		ret = of_irq_parse_raw(laddr, &out_irq[i]);
		if (ret) {
			pci_err(pdev, "parse irq %d failed, ret %d", pin, ret);
			out_irq[i].np = NULL;
			pci_dbg(pdev, "parse irq %d failed, ret %d", pin, ret);
			continue;
		}
		ret = of_property_read_u32(out_irq[i].np, "#address-cells",
		of_property_read_u32(out_irq[i].np, "#address-cells",
				     &addr_sz[i]);
		if (ret)
			addr_sz[i] = 0;
	}

	list_for_each_entry(child, &pdev->subordinate->devices, bus_list) {
		for (pin = 1; pin <= OF_PCI_MAX_INT_PIN; pin++) {
			i = pci_swizzle_interrupt_pin(child, pin) - 1;
			if (!out_irq[i].np)
				continue;
			map_sz += 5 + addr_sz[i] + out_irq[i].args_count;
		}
	}

	/*
	 * Parsing interrupt failed for all pins. In this case, it does not
	 * need to generate interrupt-map property.
	 */
	if (!map_sz)
		return 0;

	int_map = kcalloc(map_sz, sizeof(u32), GFP_KERNEL);
	mapp = int_map;

	list_for_each_entry(child, &pdev->subordinate->devices, bus_list) {
		for (pin = 1; pin <= OF_PCI_MAX_INT_PIN; pin++) {
			i = pci_swizzle_interrupt_pin(child, pin) - 1;
			if (!out_irq[i].np)
				continue;

			*mapp = (child->bus->number << 16) |
				(child->devfn << 8);
			mapp += OF_PCI_ADDRESS_CELLS;
			*mapp = pin;
			mapp++;
			i = pci_swizzle_interrupt_pin(child, pin) - 1;
			*mapp = out_irq[i].np->phandle;
			mapp++;
			if (addr_sz[i]) {