]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
PCI: dwc: ep: Prevent changing BAR size/flags in pci_epc_set_bar()
authorNiklas Cassel <cassel@kernel.org>
Fri, 13 Dec 2024 14:33:03 +0000 (15:33 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 17 Feb 2025 09:05:21 +0000 (10:05 +0100)
commit 3708acbd5f169ebafe1faa519cb28adc56295546 upstream.

In commit 4284c88fff0e ("PCI: designware-ep: Allow pci_epc_set_bar() update
inbound map address") set_bar() was modified to support dynamically
changing the backing physical address of a BAR that was already configured.

This means that set_bar() can be called twice, without ever calling
clear_bar() (as calling clear_bar() would clear the BAR's PCI address
assigned by the host).

This can only be done if the new BAR size/flags does not differ from the
existing BAR configuration. Add these missing checks.

If we allow set_bar() to set e.g. a new BAR size that differs from the
existing BAR size, the new address translation range will be smaller than
the BAR size already determined by the host, which would mean that a read
past the new BAR size would pass the iATU untranslated, which could allow
the host to read memory not belonging to the new struct pci_epf_bar.

While at it, add comments which clarifies the support for dynamically
changing the physical address of a BAR. (Which was also missing.)

Fixes: 4284c88fff0e ("PCI: designware-ep: Allow pci_epc_set_bar() update inbound map address")
Link: https://lore.kernel.org/r/20241213143301.4158431-10-cassel@kernel.org
Signed-off-by: Niklas Cassel <cassel@kernel.org>
Signed-off-by: Krzysztof WilczyƄski <kwilczynski@kernel.org>
Reviewed-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Cc: stable@vger.kernel.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/pci/controller/dwc/pcie-designware-ep.c

index 76b061c7c1f70c9f84db18cc61f6a2c9cb7aba12..b58e89ea566b8d55506a9fdfd6656364f449ce6c 100644 (file)
@@ -222,8 +222,28 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
        if ((flags & PCI_BASE_ADDRESS_MEM_TYPE_64) && (bar & 1))
                return -EINVAL;
 
-       if (ep->epf_bar[bar])
+       /*
+        * Certain EPF drivers dynamically change the physical address of a BAR
+        * (i.e. they call set_bar() twice, without ever calling clear_bar(), as
+        * calling clear_bar() would clear the BAR's PCI address assigned by the
+        * host).
+        */
+       if (ep->epf_bar[bar]) {
+               /*
+                * We can only dynamically change a BAR if the new BAR size and
+                * BAR flags do not differ from the existing configuration.
+                */
+               if (ep->epf_bar[bar]->barno != bar ||
+                   ep->epf_bar[bar]->size != size ||
+                   ep->epf_bar[bar]->flags != flags)
+                       return -EINVAL;
+
+               /*
+                * When dynamically changing a BAR, skip writing the BAR reg, as
+                * that would clear the BAR's PCI address assigned by the host.
+                */
                goto config_atu;
+       }
 
        reg = PCI_BASE_ADDRESS_0 + (4 * bar);