CVE-2024-26605 - Deadlock in Linux Kernel PCI/ASPM while Enabling Power Management (Explained & Exploitable)

If you’re running a recent Linux kernel (6.7. specifically) on Qualcomm-based laptops (like the Lenovo ThinkPad X13s), there's been a nasty little bug lurking in the PCIe Active State Power Management (ASPM) system. It caused kernel-level deadlocks—locking up your machine due to double-grab on the same semaphore during PCI bus probing. This post breaks down CVE-2024-26605, the vulnerability, where it happened in code, how it could be abused, and how the fix works.

What is CVE-2024-26605?

CVE-2024-26605 is a vulnerability in the Linux kernel’s PCI/ASPM (Active State Power Management) subsystem. A code change in kernel version 6.7-final accidentally introduced a bug: when trying to enable ASPM on certain Qualcomm PCIe controllers, the kernel could deadlock (freeze) if two threads hit the same locking path at the same time.

This lockup means your computer (or kernel task) just stops responding—not good on laptops or servers.

Root Cause: Recursive locking (double-acquire) of pci_bus_sem semaphore

- Consequence: Potential for full device/kernel lock-up

The Flow

There’s a device probe in progress (pci_walk_bus), which *already holds* a lock (pci_bus_sem). Then, during the probe, enabling ASPM (qcom_pcie_enable_aspm) tries to enter a function (pci_set_power_state) which tries to *grab the lock again* (recursively).

Here’s a real trace, with the interesting bits highlighted

WARNING: possible recursive locking detected
kworker/u16:5/90 is trying to acquire lock:
  (pci_bus_sem){++++}-{3:3}, at: pcie_aspm_pm_state_change+x58/xdc

but task is already holding lock:
  (pci_bus_sem){++++}-{3:3}, at: pci_walk_bus+x34/xbc

Call trace:
 print_deadlock_bug+x25c/x348
 __lock_acquire+x10a4/x2064
 lock_acquire+x1e8/x318
 down_read+x60/x184
 pcie_aspm_pm_state_change+x58/xdc
 pci_set_full_power_state+xa8/x114
 pci_set_power_state+xc4/x120
 qcom_pcie_enable_aspm+x1c/x3c [pcie_qcom]
 pci_walk_bus+x64/xbc
 qcom_pcie_host_post_init_2_7_+x28/x34 [pcie_qcom]

Translation:
The same lock (pci_bus_sem) is being taken twice in the same thread, which creates potential for a deadlock, especially if another thread has the lock in a different mode.

Proof of Concept (PoC): Can It Be Triggered?

The maintainers noticed this can be reproduced (triggered) easily: insert a small delay in the async probe, and another thread can sneak in, grabbing a write-lock, then your system hangs.

This is more likely to surface in high-core-count systems (laptops with multiple cores) or when device probing happens in parallel.

Here’s a sketch of the flow

// Pseudo-code illustrating the sequence
down_read(&pci_bus_sem);  // First lock (held as walk_bus starts)

pci_walk_bus() {
    // Still holding the lock
    ...
    qcom_pcie_enable_aspm() {
        pci_set_power_state() {
            down_read(&pci_bus_sem);  // Dangerous: lock acquired again
            ... // Deadlock risk here!
        }
    }
    ...
}

Since the call is happening inside a region where the lock is already acquired, a second unconditional attempt down_read(&pci_bus_sem) will block, waiting for itself—BOOM, deadlock.

Exploit Scenario

While this bug primarily causes *denial of service* by accidental deadlock, an attacker with lower-level access (e.g., ability to trigger device probes/refreshed or hotpluggable devices repeatedly) could use this deadlock as a primitive to intentionally hang a target device or server.

For instance

- If you build a kernel driver that manipulates PCI enumeration or triggers ASPM enabling quickly or at boot, you can force this path and lock up the system.
- On laptops, a rogue application convincing the kernel to rescan PCIe buses repeatedly may force the deadlock, resulting in DoS (Denial of Service).

The fix is clean and straightforward

- New helper function: pci_set_power_state_locked() which can be called when the PCI bus semaphore (pci_bus_sem) is already held (avoiding trying to grab it again).
- ASPM path updated: The Qualcomm ASPM enabling function now uses this helper when inside a locked context.

Core Patch Snippet

// New locked variant added
int pci_set_power_state_locked(struct pci_dev *dev, pci_power_t state) {
    // precondition: pci_bus_sem is already held
    ...
    // No attempt to re-acquire lock
}

// In qcom_pcie_enable_aspm(), call the locked version
pci_set_power_state_locked(pdev, PCI_D);

References & Further Reading

- Original Patch Discussion: linux-pci mailing list
- Commit Fixing CVE-2024-26605 (kernel git)
- CVE Record: CVE-2024-26605
- Linux Kernel PCI Code

Takeaways for Users & Admins

1. Update your kernel: If you’re running Linux 6.7/6.7.1 or using Qualcomm PCIe hardware, upgrade to the patched kernel.

System integrators and distro maintainers should backport the fix ASAP.

This bug doesn’t let attackers break in, but it can lock up your hardware remotely or cause headaches for anyone relying on their system’s availability.

Final Note

Kernel locking bugs like CVE-2024-26605 show how subtle kernel code can fail in parallel, high-core, or hotplug conditions. For more, follow kernel changelogs and consider regular security audits.

Stay patched, stay safe, and don’t be afraid to check the kernel mailing lists for heads-ups!

*Written for simple, clear understanding—feel free to share or reference. If you want more technical deep-dives, let us know in the comments!*

Timeline

Published on: 02/26/2024 16:28:00 UTC
Last modified on: 04/17/2024 17:47:46 UTC