[BUG] PCI: shpchp: Potential use-after-free in cleanup_slots() due to missing synchronization
From: 马云龙
Date: Sun Mar 08 2026 - 23:18:09 EST
Hi,
We found a potential use-after-free bug in the shpchp driver through
static analysis.
Summary
-------
In cleanup_slots() (drivers/pci/hotplug/shpchp_core.c), the function
uses cancel_delayed_work() instead of cancel_delayed_work_sync() before
destroying the workqueue and freeing the slot structure. This can lead
to use-after-free if the delayed work is currently executing.
Analysis
--------
cancel_delayed_work() is non-synchronous - it only cancels pending work
that has not yet started execution. If the delayed work is already
running, cancel_delayed_work() returns false and the work continues to
execute.
The vulnerable code path:
void cleanup_slots(struct controller *ctrl)
{
struct slot *slot, *next;
list_for_each_entry_safe(slot, next, &ctrl->slot_list, slot_list) {
list_del(&slot->slot_list);
cancel_delayed_work(&slot->work); // non-sync cancel
destroy_workqueue(slot->wq); // workqueue destroyed
pci_hp_deregister(&slot->hotplug_slot);
kfree(slot); // slot freed
}
}
The delayed work function shpchp_queue_pushbutton_work() (shpchp_ctrl.c)
performs the following operations that can race with cleanup:
- mutex_lock(&p_slot->lock)
- queue_work(p_slot->wq, &info->work)
- mutex_unlock(&p_slot->lock)
Race Scenario
-------------
CPU0 (driver unload) CPU1 (delayed work)
==================== ====================
shpchp_queue_pushbutton_work()
mutex_lock(&p_slot->lock)
cleanup_slots()
cancel_delayed_work() // returns false, work running
destroy_workqueue(slot->wq) // workqueue destroyed
queue_work(p_slot->wq, ...) // UAF!
kfree(slot) mutex_unlock(&p_slot->lock) // UAF!
Trigger Condition
-----------------
1. User presses the PCI hotplug attention button
2. handle_button_press_event() queues a 5-second delayed work
3. Within 5 seconds, driver is unloaded or device is removed
4. cleanup_slots() races with the executing delayed work
Impact
------
- Kernel crash/panic
- Potential privilege escalation if freed memory is reallocated
Reference
---------
The similar pciehp driver correctly uses cancel_delayed_work_sync() in
pciehp_release_ctrl() (drivers/pci/hotplug/pciehp_hpc.c:1098) for the
same pattern.
Suggested Fix
-------------
Replace cancel_delayed_work() with cancel_delayed_work_sync():
- cancel_delayed_work(&slot->work);
+ cancel_delayed_work_sync(&slot->work);
Detection Information
---------------------
- Tool: ConCord (static analysis framework for concurrency bugs)
- Kernel Version: v7.0-rc2 (commit 0031c06807cf)
- Pattern: Delayed work teardown without synchronization
- Confidence: High
Thanks,
Yunlong Ma