[PATCH 3/5] PCI/ASPM: Skip link-state setup without downstream function 0

From: shao.mingyin

Date: Fri Nov 06 2026 - 11:28:41 EST


From: Shao Mingyin <shao.mingyin@xxxxxxxxxx>

pcie_aspm_init_link_state() builds a link state for a downstream port
whose subordinate bus is non-empty. For a bridge whose bus has devices
but no function 0 -- a multi-function device with function 0 hidden, or
functions isolated by a hypervisor -- pci_function_0() returns NULL, so
alloc_pcie_link_state() sets link->downstream to NULL.

pcie_aspm_cap_init() then dereferences child->aspm_l0s_support, and even
if that site is guarded the same NULL link->downstream is dereferenced
again by pcie_config_aspm_link() (reached through pcie_config_aspm_path())
and by pcie_aspm_check_latency(), so the link cannot be safely handled at
all.

Skip the whole link-state setup when the downstream bus has no function 0;
nothing below (capability init, latency check, link configuration) then
sees a NULL downstream.

Fixes: 3bd7db63a841 ("PCI/ASPM: Always set link->downstream to avoid NULL dereference on remove")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Shao Mingyin <shao.mingyin@xxxxxxxxxx>
---
Tested on 7.3.0-next-20261030 (QEMU/TCG x86_64, q35) with a PCIe root port
whose downstream would have devices but no function 0, emulated by a
TEST-ONLY hook (applied to both kernels) that makes pci_function_0()
return NULL for the QEMU root port; QEMU cannot model a function without
its function 0. No extra hardware is needed.

Unpatched, pcie_aspm_cap_init() dereferences child->aspm_l0s_support with
link->downstream == NULL and the kernel panics. A guard placed only in
pcie_aspm_cap_init() is not sufficient: with the default (performance)
policy the same NULL is dereferenced again in pcie_config_aspm_link() via
pcie_config_aspm_path(). With this patch pcie_aspm_init_link_state()
skips link-state setup entirely and the kernel boots cleanly, the endpoint
probes normally and the guest powers off.

unpatched patched
BUG: kernel NULL pointer dereference, no fault; guest powers off
address: 0xc0 (aspm_l0s_support)

diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
index b039b57f8466..003b13db5850 100644
--- a/drivers/pci/pcie/aspm.c
+++ b/drivers/pci/pcie/aspm.c
@@ -1243,6 +1243,9 @@ void pcie_aspm_init_link_state(struct pci_dev *pdev)
if (list_empty(&pdev->subordinate->devices))
goto out;

+ if (!pci_function_0(pdev->subordinate))
+ goto out;
+
mutex_lock(&aspm_lock);
link = alloc_pcie_link_state(pdev);
if (!link)