Windows: harden work item completion drain before queue cleanup

Decrement ActiveWorkItems only after the completion work item has returned queue resources and released its pool item. EncryptedIoQueueStop now synchronizes with WorkItemLock after the active count drains, ensuring the last work item has stopped touching queue state before the work item pool and buffer pools are freed.
This commit is contained in:
Mounir IDRASSI
2026-07-02 21:46:14 +09:00
parent cbcf5339f6
commit 5e091eabfc
+22 -6
View File
@@ -422,12 +422,6 @@ static VOID CompleteIrpWorkItemRoutine(PDEVICE_OBJECT DeviceObject, PVOID Contex
}
__finally
{
// If no active work items remain, signal the event
if (InterlockedDecrement(&queue->ActiveWorkItems) == 0)
{
KeSetEvent(&queue->NoActiveWorkItemsEvent, IO_DISK_INCREMENT, FALSE);
}
// Return the work item to the free list
KeAcquireSpinLock(&queue->WorkItemLock, &oldIrql);
InsertTailList(&queue->FreeWorkItemsList, &workItem->ListEntry);
@@ -438,6 +432,19 @@ static VOID CompleteIrpWorkItemRoutine(PDEVICE_OBJECT DeviceObject, PVOID Contex
// Free the item
ReleasePoolBuffer(queue, item);
// Decrement ActiveWorkItems last: once it reaches zero,
// EncryptedIoQueueStop frees the work item pool and buffer pools, so
// this routine must not touch queue resources afterwards. The
// decrement and signal are done under WorkItemLock, which Stop
// re-acquires after draining, guaranteeing this routine has left the
// protected region before anything is freed.
KeAcquireSpinLock(&queue->WorkItemLock, &oldIrql);
if (InterlockedDecrement(&queue->ActiveWorkItems) == 0)
{
KeSetEvent(&queue->NoActiveWorkItemsEvent, IO_DISK_INCREMENT, FALSE);
}
KeReleaseSpinLock(&queue->WorkItemLock, oldIrql);
}
}
@@ -1508,6 +1515,15 @@ NTSTATUS EncryptedIoQueueStop (EncryptedIoQueue *queue)
KeResetEvent(&queue->NoActiveWorkItemsEvent);
}
// The last work item drops ActiveWorkItems to zero while holding
// WorkItemLock; acquiring it here ensures that work item has stopped
// touching queue resources before they are freed below.
{
KIRQL oldIrql;
KeAcquireSpinLock(&queue->WorkItemLock, &oldIrql);
KeReleaseSpinLock(&queue->WorkItemLock, oldIrql);
}
// Free pre-allocated work items
for (ULONG i = 0; i < queue->MaxWorkItems; ++i)
{