When an AER error occurs, candidate error-source devices are identified
and recorded into e_info->dev[] via add_error_device(), which increments
each device's reference count with pci_dev_get().

If is_error_source() matches a device purely by Requester/Completer ID
match (e_info->id == pci_dev_id(dev)), the device is added to e_info->dev[]
even if it lacks the AER extended capability (dev->aer_cap == 0).

Later, during aer_process_err_devices(), aer_get_device_error_info()
returns 0 when dev->aer_cap is 0 (or if no active error status is read),
causing aer_process_err_devices() to skip handle_error_source().
Previously, handle_error_source() was responsible for calling
pci_dev_put(dev). When handle_error_source() was skipped, pci_dev_put()
was never invoked, permanently leaking the struct pci_dev reference.

Decouple device reference release from error handling by removing
pci_dev_put() from handle_error_source() and calling pci_dev_put()
unconditionally for all recorded devices in aer_process_err_devices().

Fixes: 1ab4a3c80508 ("PCI/AER: Stop ruling out unbound devices as error source")
Signed-off-by: Priyank Rathod <[email protected]>
---
When native PCIe Advanced Error Reporting (AER) handles an error signaled
by a Root Port or Root Complex Event Collector (RCEC) via
aer_isr_one_error_type(), candidate error-source devices are identified
by walking the downstream hierarchy in find_source_device().

During this walk, each matching device is enqueued into e_info->dev[] by
add_error_device(), which increments the device's reference count via
pci_dev_get(dev).

1. The Vulnerable Use Cases:
============================
is_error_source() matches devices through two distinct mechanisms:
  a) Explicit Requester/Completer ID match:
     if (e_info->id == pci_dev_id(dev))
         return true;
  b) Uncorrectable/Correctable status register inspection across the hierarchy.

On the ID-match fast path (a), is_error_source() returns true without
checking whether the device implements the AER extended capability
(dev->aer_cap != 0). This creates a real, non-exotic situation in several
common hardware topologies and operational states:

  - Non-AER Endpoints: Simple or legacy PCIe endpoints (e.g. basic serial,
    sensor, audio, or older controller ICs) that implement standard PCIe
    capabilities (0x10) but omit the AER Extended Capability (0x0001) in
    extended configuration space.
  - Multi-function PCIe Devices: Multi-function endpoints where AER is only
    implemented on Function 0, but an error is routed or attributed to
    Function 1..7 which have dev->aer_cap == 0.
  - Devices behind PCIe-to-PCI/PCI-X Bridges: Conventional PCI devices
    aliased under a bridge's Requester ID or lacking AER registers.
  - Unbound Devices: Following commit 1ab4a3c80508 ("PCI/AER: Stop ruling out
    unbound devices as error source"), devices with dev->enable_cnt == 0 are
    no longer filtered out. An unbound, un-configured device without AER
    registers that triggers link-level errors is matched purely by ID.
  - Transient Errors / Zero Status: Devices whose AER status registers read
    as 0 or return ~0 (link down / device in D3) during config read in
    aer_get_device_error_info().

2. The Refcount Leak Mechanics:
===============================
When any of the above conditions occur:
  1. add_error_device() takes a reference: e_info->dev[i] = pci_dev_get(dev).
  2. aer_process_err_devices() iterates over e_info->dev[i] and calls
     aer_get_device_error_info(e_info, i).
  3. aer_get_device_error_info() checks 'if (!aer) return 0;' and immediately
     returns 0 because dev->aer_cap == 0.
  4. aer_process_err_devices() evaluates:
       if (aer_get_device_error_info(e_info, i))
           handle_error_source(e_info->dev[i], e_info);
     Because aer_get_device_error_info() returned 0, handle_error_source()
     is completely bypassed.
  5. Previously, handle_error_source() was the sole owner of pci_dev_put(dev).
     Bypassing handle_error_source() leaves the reference taken in step (1)
     unreleased.

3. Impact:
==========
Because the struct pci_dev reference count never reaches zero:
  - The struct device release callback pci_release_dev() is never invoked.
  - Dynamic device resources, sysfs entries, DMA mappings, and aer_info
    remain allocated in memory, even after the device is hot-unplugged or
    removed via sysfs ('echo 1 > /sys/bus/pci/devices/.../remove').
  - Under link instability or an error storm where errors are repeatedly
    attributed to a non-AER device, each error event leaks one pci_dev
    reference, leading to unbounded refcount inflation and memory leaks.

4. Proposed Fix:
================
Decouple device reference lifetime management from error reporting:
  - Remove pci_dev_put() from handle_error_source().
  - Unconditionally invoke pci_dev_put(e_info->dev[i]) in
    aer_process_err_devices() for every candidate device recorded in
    e_info->dev[] across the loop.

This ensures every pci_dev_get() in add_error_device() has a strict,
guaranteed 1:1 lifecycle match in aer_process_err_devices(), irrespective of
device capability presence, status bit reads, or driver binding state.
---
 drivers/pci/pcie/aer.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c
index c4fd9c0b2a54..2401b3a18f07 100644
--- a/drivers/pci/pcie/aer.c
+++ b/drivers/pci/pcie/aer.c
@@ -1197,7 +1197,6 @@ static void handle_error_source(struct pci_dev *dev, 
struct aer_err_info *info)
 {
        cxl_rch_handle_error(dev, info);
        pci_aer_handle_error(dev, info);
-       pci_dev_put(dev);
 }
 
 #ifdef CONFIG_ACPI_APEI_PCIEAER
@@ -1361,6 +1360,7 @@ static inline void aer_process_err_devices(struct 
aer_err_info *e_info)
        for (i = 0; i < e_info->error_dev_num && e_info->dev[i]; i++) {
                if (aer_get_device_error_info(e_info, i))
                        handle_error_source(e_info->dev[i], e_info);
+               pci_dev_put(e_info->dev[i]);
        }
 }
 

---
base-commit: 0f23d56f17fdfc7db69d51f64c8b91bbab947aa9
change-id: 20260830-fix-aer-refcount-leak-6378f84d62ba

Best regards,
-- 
Priyank Rathod <[email protected]>


Reply via email to