From 5e091eabfcc77b184357d5ff75ce1ce96cd80bc0 Mon Sep 17 00:00:00 2001 From: Mounir IDRASSI Date: Thu, 2 Jul 2026 21:46:14 +0900 Subject: [PATCH] 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. --- src/Driver/EncryptedIoQueue.c | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) 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) {