diff --git a/src/Driver/EncryptedIoQueue.c b/src/Driver/EncryptedIoQueue.c index ed2c9478..ec7f5a9a 100644 --- a/src/Driver/EncryptedIoQueue.c +++ b/src/Driver/EncryptedIoQueue.c @@ -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) {