From 5b532855453cb2720d6512f7022aff0aa6698110 Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Tue, 28 Jul 2026 15:36:48 +0300 Subject: [PATCH] sys: FspMainFileClose: fix issue #680 Instead of closing the main file handle in the dispatcher thread, close it in a system thread and wait for that close to complete in an alertable manner. This avoids a deadlock due to the file system process being forcibly terminated via NtTerminateProcess. --- src/sys/driver.h | 8 +++++-- src/sys/file.c | 36 +++++++++++++++++++++++++++++--- src/sys/util.c | 54 +++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 86 insertions(+), 12 deletions(-) diff --git a/src/sys/driver.h b/src/sys/driver.h index e52949c2..cbec7272 100644 --- a/src/sys/driver.h +++ b/src/sys/driver.h @@ -698,14 +698,18 @@ NTSTATUS FspOplockFsctrl( /* utility: synchronous work queue */ typedef struct { - KEVENT Event; + LONG RefCount; PWORKER_THREAD_ROUTINE Routine; PVOID Context; + KEVENT Event; WORK_QUEUE_ITEM WorkQueueItem; } FSP_SYNCHRONOUS_WORK_ITEM; +FSP_SYNCHRONOUS_WORK_ITEM *FspAllocateSynchronousWorkItem( + PWORKER_THREAD_ROUTINE Routine, PVOID Context); VOID FspInitializeSynchronousWorkItem(FSP_SYNCHRONOUS_WORK_ITEM *SynchronousWorkItem, PWORKER_THREAD_ROUTINE Routine, PVOID Context); -VOID FspExecuteSynchronousWorkItem(FSP_SYNCHRONOUS_WORK_ITEM *SynchronousWorkItem); +NTSTATUS FspExecuteSynchronousWorkItem(FSP_SYNCHRONOUS_WORK_ITEM *SynchronousWorkItem, + BOOLEAN Alertable); /* utility: delayed work queue */ typedef struct diff --git a/src/sys/file.c b/src/sys/file.c index 3231943b..0bdc6ba0 100644 --- a/src/sys/file.c +++ b/src/sys/file.c @@ -121,6 +121,7 @@ NTSTATUS FspMainFileOpen( NTSTATUS FspMainFileClose( HANDLE MainFileHandle, PFILE_OBJECT MainFileObject); +static WORKER_THREAD_ROUTINE FspMainFileCloseSynchronousWork; VOID FspFileNodeOplockPrepare(PVOID Context, PIRP Irp); VOID FspFileNodeOplockComplete(PVOID Context, PIRP Irp); @@ -183,6 +184,7 @@ VOID FspFileNodeOplockComplete(PVOID Context, PIRP Irp); #pragma alloc_text(PAGE, FspFileDescSetDirectoryMarker) #pragma alloc_text(PAGE, FspMainFileOpen) #pragma alloc_text(PAGE, FspMainFileClose) +#pragma alloc_text(PAGE, FspMainFileCloseSynchronousWork) #pragma alloc_text(PAGE, FspFileNodeOplockPrepare) #pragma alloc_text(PAGE, FspFileNodeOplockComplete) #endif @@ -2803,14 +2805,42 @@ NTSTATUS FspMainFileClose( if (0 != MainFileHandle) { - Result = ObCloseHandle(MainFileHandle, KernelMode); - if (!NT_SUCCESS(Result)) - DEBUGLOG("ObCloseHandle() = %s", NtStatusSym(Result)); + FSP_SYNCHRONOUS_WORK_ITEM *WorkItem; + + WorkItem = DEBUGTEST(90) ? + FspAllocateSynchronousWorkItem(FspMainFileCloseSynchronousWork, MainFileHandle) : + 0; + if (0 != WorkItem) + /* + * Fix issue #680: + * Instead of closing the main file handle in this thread, close it in a system thread + * and wait for that close to complete in an alertable manner. This avoids a deadlock + * due to the file system process being forcibly terminated via NtTerminateProcess. + */ + Result = FspExecuteSynchronousWorkItem(WorkItem, TRUE); + else + /* + * Best effort: Close the main file handle in this thread. + * Small chance of deadlock if someone is killing our file system. + */ + FspMainFileCloseSynchronousWork(MainFileHandle); } return Result; } +static VOID FspMainFileCloseSynchronousWork(PVOID Context) +{ + PAGED_CODE(); + + HANDLE MainFileHandle = Context; + NTSTATUS Result; + + Result = ObCloseHandle(MainFileHandle, KernelMode); + if (!NT_SUCCESS(Result)) + DEBUGLOG("ObCloseHandle() = %s", NtStatusSym(Result)); +} + VOID FspFileNodeOplockPrepare(PVOID Context, PIRP Irp) { PAGED_CODE(); diff --git a/src/sys/util.c b/src/sys/util.c index 996ff898..45055035 100644 --- a/src/sys/util.c +++ b/src/sys/util.c @@ -115,9 +115,12 @@ NTSTATUS FspOplockFsctrl( POPLOCK Oplock, PIRP Irp, ULONG OpenCount); +FSP_SYNCHRONOUS_WORK_ITEM *FspAllocateSynchronousWorkItem( + PWORKER_THREAD_ROUTINE Routine, PVOID Context); VOID FspInitializeSynchronousWorkItem(FSP_SYNCHRONOUS_WORK_ITEM *SynchronousWorkItem, PWORKER_THREAD_ROUTINE Routine, PVOID Context); -VOID FspExecuteSynchronousWorkItem(FSP_SYNCHRONOUS_WORK_ITEM *SynchronousWorkItem); +NTSTATUS FspExecuteSynchronousWorkItem(FSP_SYNCHRONOUS_WORK_ITEM *SynchronousWorkItem, + BOOLEAN Alertable); static WORKER_THREAD_ROUTINE FspExecuteSynchronousWorkItemRoutine; VOID FspInitializeDelayedWorkItem(FSP_DELAYED_WORK_ITEM *DelayedWorkItem, PWORKER_THREAD_ROUTINE Routine, PVOID Context); @@ -167,6 +170,7 @@ LONG FspCompareUnicodeString( #pragma alloc_text(PAGE, FspCheckOplock) #pragma alloc_text(PAGE, FspCheckOplockEx) #pragma alloc_text(PAGE, FspOplockFsctrl) +#pragma alloc_text(PAGE, FspAllocateSynchronousWorkItem) #pragma alloc_text(PAGE, FspInitializeSynchronousWorkItem) #pragma alloc_text(PAGE, FspExecuteSynchronousWorkItem) #pragma alloc_text(PAGE, FspExecuteSynchronousWorkItemRoutine) @@ -1123,27 +1127,56 @@ NTSTATUS FspOplockFsctrl( return Result; } +FSP_SYNCHRONOUS_WORK_ITEM *FspAllocateSynchronousWorkItem( + PWORKER_THREAD_ROUTINE Routine, PVOID Context) +{ + PAGED_CODE(); + + FSP_SYNCHRONOUS_WORK_ITEM *SynchronousWorkItem; + + SynchronousWorkItem = FspAllocNonPaged(sizeof *SynchronousWorkItem); + if (0 != SynchronousWorkItem) + { + FspInitializeSynchronousWorkItem(SynchronousWorkItem, Routine, Context); + SynchronousWorkItem->RefCount = 2; /* make it refcounted */ + } + + return SynchronousWorkItem; +} + VOID FspInitializeSynchronousWorkItem(FSP_SYNCHRONOUS_WORK_ITEM *SynchronousWorkItem, PWORKER_THREAD_ROUTINE Routine, PVOID Context) { PAGED_CODE(); - KeInitializeEvent(&SynchronousWorkItem->Event, NotificationEvent, FALSE); + SynchronousWorkItem->RefCount = 0; /* initialize as non-refcounted */ SynchronousWorkItem->Routine = Routine; SynchronousWorkItem->Context = Context; + KeInitializeEvent(&SynchronousWorkItem->Event, NotificationEvent, FALSE); ExInitializeWorkItem(&SynchronousWorkItem->WorkQueueItem, FspExecuteSynchronousWorkItemRoutine, SynchronousWorkItem); } -VOID FspExecuteSynchronousWorkItem(FSP_SYNCHRONOUS_WORK_ITEM *SynchronousWorkItem) +NTSTATUS FspExecuteSynchronousWorkItem(FSP_SYNCHRONOUS_WORK_ITEM *SynchronousWorkItem, + BOOLEAN Alertable) { PAGED_CODE(); - ExQueueWorkItem(&SynchronousWorkItem->WorkQueueItem, CriticalWorkQueue); - NTSTATUS Result; - Result = KeWaitForSingleObject(&SynchronousWorkItem->Event, Executive, KernelMode, FALSE, 0); - ASSERT(STATUS_SUCCESS == Result); + + /* Alertable=TRUE requires item allocated by FspAllocateSynchronousWorkItem */ + ASSERT(!Alertable || 0 < SynchronousWorkItem->RefCount); + + ExQueueWorkItem(&SynchronousWorkItem->WorkQueueItem, CriticalWorkQueue); + Result = KeWaitForSingleObject(&SynchronousWorkItem->Event, Executive, KernelMode, Alertable, 0); + + if (0 < SynchronousWorkItem->RefCount) + { + if (0 == InterlockedDecrement(&SynchronousWorkItem->RefCount)) + FspFree(SynchronousWorkItem); + } + + return Result; } static VOID FspExecuteSynchronousWorkItemRoutine(PVOID Context) @@ -1151,8 +1184,15 @@ static VOID FspExecuteSynchronousWorkItemRoutine(PVOID Context) PAGED_CODE(); FSP_SYNCHRONOUS_WORK_ITEM *SynchronousWorkItem = Context; + SynchronousWorkItem->Routine(SynchronousWorkItem->Context); KeSetEvent(&SynchronousWorkItem->Event, 1, FALSE); + + if (0 < SynchronousWorkItem->RefCount) + { + if (0 == InterlockedDecrement(&SynchronousWorkItem->RefCount)) + FspFree(SynchronousWorkItem); + } } VOID FspInitializeDelayedWorkItem(FSP_DELAYED_WORK_ITEM *DelayedWorkItem,