PnP & Power
Plug and Play and Power Management IRP handling creates attack surface through device state transitions, where race conditions between removal, power changes, and in-flight I/O lead to use-after-free and null pointer dereference vulnerabilities.
Attack Surface Overview
- PnP entry points:
IRP_MN_REMOVE_DEVICE,IRP_MN_SURPRISE_REMOVAL,IRP_MN_QUERY_REMOVE_DEVICE,IRP_MN_STOP_DEVICE,IRP_MN_QUERY_DEVICE_RELATIONSdispatched through the driver'sIRP_MJ_PNPhandler - Power entry points:
IRP_MN_SET_POWER,IRP_MN_QUERY_POWER,IRP_MN_WAIT_WAKEdispatched throughIRP_MJ_POWERhandler - Synchronization primitives:
IoAcquireRemoveLock/IoReleaseRemoveLockpattern for coordinating removal with in-flight I/O;PoRequestPowerIrpfor asynchronous power state transitions - User-mode reach: Device enable/disable via Device Manager,
SetupDi*APIs,devcon.execommands, USB device insertion/removal,powercfgcommands, system sleep/resume/hibernate/shutdown - Device interface notifications:
IoRegisterDeviceInterfacepublishes device interfaces that user-mode discovers viaCM_Register_NotificationorSetupDiGetClassDevs, creating a registration/deregistration race window - Key risk: Use-after-free conditions when device extension memory is freed during
IRP_MN_REMOVE_DEVICEwhile another thread is still processing I/O through the same device object
Mechanism Deep-Dive
The PnP Manager orchestrates the lifecycle of every hardware device in the system. When a device is physically removed or software-disabled, the PnP Manager sends an IRP_MN_SURPRISE_REMOVAL IRP followed by IRP_MN_REMOVE_DEVICE once all open handles are closed. The driver must stop processing I/O, release hardware resources, and free its device extension during removal. The fundamental problem is that I/O dispatch routines and removal handlers execute concurrently -- a thread may be in the middle of processing an IOCTL using data from the device extension while another thread processes the removal IRP and frees that same device extension memory.
The Windows Driver Model addresses this with the Remove Lock pattern: the driver acquires the remove lock at the start of every I/O dispatch, and releases it upon completion. The removal handler calls IoReleaseRemoveLockAndWait, which blocks until all outstanding I/O completes. However, many drivers implement this pattern incorrectly or omit it entirely. A missing remove lock acquisition on even one dispatch path means that removal can proceed while that I/O is in flight, freeing structures the I/O path is actively using. This is not merely a reliability issue -- an attacker who can control the timing of device removal (e.g., via devcon disable or USB eject) while simultaneously sending IOCTLs can trigger a use-after-free with controlled allocation patterns.
Power management introduces similar races. When the system transitions to sleep (S3/S4), the Power Manager sends IRP_MN_SET_POWER with a target device state of D3 (powered off). The driver must save hardware state and stop accessing the device. If an I/O dispatch routine accesses hardware registers without first checking the current power state, it may access unmapped MMIO regions or stale device state. The reverse transition (resume from sleep) requires re-initializing hardware before processing I/O, and a race between the power-up IRP completion and queued I/O requests can cause similar issues. Resource rebalance scenarios (IRP_MN_STOP_DEVICE followed by IRP_MN_START_DEVICE with new resources) add yet another dimension, as the driver must handle re-mapping of I/O ports and memory ranges while potentially having in-flight I/O referencing old mappings.
Surprise removal is the most dangerous PnP transition from a security perspective. Unlike orderly removal (which queries the driver first), surprise removal occurs without warning when hardware is physically disconnected. The driver must immediately invalidate all shared state -- cancel pending timers, flush DPC queues, complete pending IRPs with error status, and set a flag preventing new I/O dispatch. Any pending DPC routine, timer callback, or work item that fires after the device extension is freed becomes a use-after-free primitive.
Common Vulnerability Patterns
- Missing remove lock: The driver does not call
IoAcquireRemoveLockat the beginning of I/O dispatch, allowingIRP_MN_REMOVE_DEVICEto free the device extension while I/O is in progress, resulting in a use-after-free - Incomplete surprise removal cleanup: The
IRP_MN_SURPRISE_REMOVALhandler does not invalidate shared state (pending timers, DPC routines, work items), leaving dangling references that fire after the device extension is freed - Device-removed flag not checked: I/O dispatch proceeds without checking a
DeviceRemovedorDevicePnPStateflag, accessing hardware or device extension fields after removal has begun - Power state not validated: Driver accesses hardware MMIO registers without verifying the device is in the D0 (working) power state, causing bugcheck on unmapped memory access
- PnP resource rebalance race: Resource rebalance (
IRP_MN_STOP_DEVICEfollowed byIRP_MN_START_DEVICE) re-maps hardware resources while I/O is still referencing the old mappings - Device interface deregistration race:
IoSetDeviceInterfaceState(FALSE)called during removal, but user-mode threads that already opened handles via the interface continue sending I/O - Pending DPC or timer not cancelled: Removal handler does not cancel outstanding
KeSetTimerorIoQueueWorkItementries, which fire after the device object and extension are freed - Power IRP completion race: The driver completes a power-up IRP and begins processing queued I/O before hardware re-initialization is fully complete
- Child device enumeration race: Bus driver's
IRP_MN_QUERY_DEVICE_RELATIONShandler accesses child PDO list concurrently with child device removal, causing list corruption
Driver Examples
Any PnP driver is affected, but USB device drivers are most susceptible due to frequent hotplug scenarios. Storage drivers (disk.sys, USB mass storage), display drivers (GPU hotplug, display reconfiguration), audio drivers (portcls.sys miniports), and Bluetooth drivers (bthusb.sys, bthport.sys) experience frequent PnP transitions. Bus drivers (usbhub.sys, pci.sys, acpi.sys) manage child device lifetimes and must handle cascading removal correctly. Third-party drivers for removable hardware (USB-serial adapters, software-defined radio dongles, USB Ethernet adapters) frequently have PnP race bugs because testing rarely covers surprise removal under I/O load. Streaming proxy drivers like mskssrv.sys have cross-process handle semantics that complicate PnP lifetime management.
Detection Approach
- Driver Verifier: Enable the "I/O Verification" and "Force Pending I/O Requests" options in Driver Verifier to catch missing remove locks and improper IRP completion. The "Enhanced I/O Verification" option validates PnP and power IRP sequencing.
- Stress testing: Use
devcon disable/enablein a loop while simultaneously sending I/O to the device from multiple threads. Physical hotplug testing under I/O load exposes surprise removal races. Sleep/resume cycles (powercfg /hibernate on && shutdown /h) under I/O load expose power transition races. - Static analysis: Search for
IRP_MJ_PNPandIRP_MJ_POWERhandlers. Verify that all I/O dispatch paths callIoAcquireRemoveLockbefore accessing device extension data. Check that the removal handler cancels all timers, DPCs, and work items. - Concurrency analysis: Identify all asynchronous callbacks (timers, DPCs, work items) registered by the driver and verify each is properly cancelled or flushed during the
IRP_MN_SURPRISE_REMOVALandIRP_MN_REMOVE_DEVICEhandlers. - Patch diffing: PnP/power fixes typically add remove lock calls, device-state checks, timer cancellation, or work item flush calls to existing dispatch routines.
Related CVEs
| CVE | Driver | Description |
|---|---|---|
| CVE-2024-38106 | ntoskrnl.exe |
Race condition during process state transition exploitable via PnP-related timing |
| CVE-2023-36802 | mskssrv.sys |
Use-after-free in streaming proxy due to object lifetime management failure |
| CVE-2024-30089 | Microsoft Streaming Service |
Race condition in device object handling |
| CVE-2023-29360 | mskssrv.sys |
Incorrect access mode in MDL probing during cross-process streaming |
AutoPiff Detection
io_remove_lock_added--IoAcquireRemoveLock/IoReleaseRemoveLockcalls added to I/O dispatch path for PnP safetysurprise_removal_guard_added-- Device-removed state flag check added before accessing device extension or hardware resourcespower_state_validation_added-- Power state (D0) validation added before hardware register accesstimer_cancellation_on_removal--KeCancelTimerorIoStopTimercall added to PnP removal handlerworkitem_flush_on_removal-- Work item flush or synchronous wait added to device removal path to drain pending callbacks