/*++ Copyright (c) 1989 Microsoft Corporation Module Name: ioverifier.c Abstract: This module contains the routines to verify suspect drivers. Author: Narayanan Ganapathy (narg) 8-Jan-1999 Revision History: Adrian J. Oney (AdriaO) 28-Feb-1999 - merge in special irp code. --*/ #include "iomgr.h" #include "malloc.h" #include "..\verifier\vfdeadlock.h" #if (( defined(_X86_) ) && ( FPO )) #pragma optimize( "y", off ) // disable FPO for consistent stack traces #endif #define IO_FREE_IRP_TYPE_INVALID 1 #define IO_FREE_IRP_NOT_ASSOCIATED_WITH_THREAD 2 #define IO_CALL_DRIVER_IRP_TYPE_INVALID 3 #define IO_CALL_DRIVER_INVALID_DEVICE_OBJECT 4 #define IO_CALL_DRIVER_IRQL_NOT_EQUAL 5 #define IO_COMPLETE_REQUEST_INVALID_STATUS 6 #define IO_COMPLETE_REQUEST_CANCEL_ROUTINE_SET 7 #define IO_BUILD_FSD_REQUEST_EXCEPTION 8 #define IO_BUILD_IOCTL_REQUEST_EXCEPTION 9 #define IO_REINITIALIZING_TIMER_OBJECT 10 #define IO_INVALID_HANDLE 11 #define IO_INVALID_STACK_IOSB 12 #define IO_INVALID_STACK_EVENT 13 #define IO_COMPLETE_REQUEST_INVALID_IRQL 14 // // 0x200 and up are defined in ioassert.c // #ifdef IOV_KD_PRINT #define IovpKdPrint(x) KdPrint(x) #else #define IovpKdPrint(x) #endif BOOLEAN IovpValidateDeviceObject( IN PDEVICE_OBJECT DeviceObject ); VOID IovFreeIrpPrivate( IN PIRP Irp ); NTSTATUS IovpUnloadDriver( PDRIVER_OBJECT DriverObject ); BOOLEAN IovpBuildDriverObjectList( IN PVOID Object, IN PUNICODE_STRING ObjectName, IN ULONG HandleCount, IN ULONG PointerCount, IN PVOID Parameter ); NTSTATUS IovpLocalCompletionRoutine( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ); typedef struct _IOV_COMPLETION_CONTEXT { PIO_STACK_LOCATION StackPointer; PVOID IrpContext; PVOID GlobalContext; PIO_COMPLETION_ROUTINE CompletionRoutine; IO_STACK_LOCATION OldStackContents; } IOV_COMPLETION_CONTEXT, *PIOV_COMPLETION_CONTEXT; #ifdef ALLOC_PRAGMA #pragma alloc_text(INIT, IoVerifierInit) #pragma alloc_text(PAGEVRFY,IovAllocateIrp) #pragma alloc_text(PAGEVRFY,IovFreeIrp) #pragma alloc_text(PAGEVRFY,IovCallDriver) #pragma alloc_text(PAGEVRFY,IovCompleteRequest) #pragma alloc_text(PAGEVRFY,IovpValidateDeviceObject) #pragma alloc_text(PAGEVRFY,IovFreeIrpPrivate) #pragma alloc_text(PAGEVRFY,IovUnloadDrivers) #pragma alloc_text(PAGEVRFY,IovpUnloadDriver) #pragma alloc_text(PAGEVRFY,IovBuildDeviceIoControlRequest) #pragma alloc_text(PAGEVRFY,IovBuildAsynchronousFsdRequest) #pragma alloc_text(PAGEVRFY,IovpCompleteRequest) #pragma alloc_text(PAGEVRFY,IovpBuildDriverObjectList) #pragma alloc_text(PAGEVRFY,IovInitializeIrp) #pragma alloc_text(PAGEVRFY,IovCancelIrp) #pragma alloc_text(PAGEVRFY,IovAttachDeviceToDeviceStack) #pragma alloc_text(PAGEVRFY,IovInitializeTimer) #pragma alloc_text(PAGEVRFY,IovDetachDevice) #pragma alloc_text(PAGEVRFY,IovDeleteDevice) #pragma alloc_text(PAGEVRFY,IovpLocalCompletionRoutine) #endif BOOLEAN IopVerifierOn = FALSE; ULONG IovpVerifierLevel = (ULONG)0; LONG IovpInitCalled = 0; ULONG IovpVerifierFlags = 0; // Stashes the verifier flags passed at init. #ifdef ALLOC_DATA_PRAGMA #pragma data_seg("INITDATA") #endif BOOLEAN IoVerifierOnByDefault = TRUE; #ifdef ALLOC_DATA_PRAGMA #pragma data_seg() #endif VOID IoVerifierInit( IN ULONG VerifierFlags ) { IovpVerifierLevel = 2; if (IoVerifierOnByDefault) { VerifierFlags |= DRIVER_VERIFIER_IO_CHECKING; } VfInitVerifier(VerifierFlags); if (!VerifierFlags) { return; } pIoAllocateIrp = IovAllocateIrp; if (!(VerifierFlags & DRIVER_VERIFIER_IO_CHECKING)) { if (!(VerifierFlags & DRIVER_VERIFIER_DEADLOCK_DETECTION) && !(VerifierFlags & DRIVER_VERIFIER_DMA_VERIFIER)) { return; } else { // // If deadlock or DMA verifier are on we need to let the function // continue to install the hooks but we will set the // I/O verifier level to minimal checks. // IovpVerifierLevel = 0; } } // // Enable and hook in the verifier. // IopVerifierOn = TRUE; IovpInitCalled = 1; IovpVerifierFlags = VerifierFlags; // // Initialize the special IRP code as appropriate. // InterlockedExchangePointer((PVOID *)&pIofCallDriver, (PVOID) IovCallDriver); InterlockedExchangePointer((PVOID *)&pIofCompleteRequest, (PVOID) IovCompleteRequest); InterlockedExchangePointer((PVOID *)&pIoFreeIrp, (PVOID) IovFreeIrpPrivate); ASSERT(KeGetCurrentIrql() <= APC_LEVEL); } BOOLEAN IovpValidateDeviceObject( IN PDEVICE_OBJECT DeviceObject ) { if ((DeviceObject->Type != IO_TYPE_DEVICE) || (DeviceObject->DriverObject == NULL) || (DeviceObject->ReferenceCount < 0 )) { return FALSE; } else { return TRUE; } } VOID IovFreeIrp( IN PIRP Irp ) { IovFreeIrpPrivate(Irp); } VOID IovFreeIrpPrivate( IN PIRP Irp ) { BOOLEAN freeHandled ; if (IopVerifierOn) { if (Irp->Type != IO_TYPE_IRP) { KeBugCheckEx(DRIVER_VERIFIER_IOMANAGER_VIOLATION, IO_FREE_IRP_TYPE_INVALID, (ULONG_PTR)Irp, 0, 0); } if (!IsListEmpty(&(Irp)->ThreadListEntry)) { KeBugCheckEx(DRIVER_VERIFIER_IOMANAGER_VIOLATION, IO_FREE_IRP_NOT_ASSOCIATED_WITH_THREAD, (ULONG_PTR)Irp, 0, 0); } } VerifierIoFreeIrp(Irp, &freeHandled); if (freeHandled) { return; } IopFreeIrp(Irp); } NTSTATUS FASTCALL IovCallDriver( IN PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp ) { KIRQL saveIrql; NTSTATUS status; PIOFCALLDRIVER_STACKDATA iofCallDriverStackData; BOOLEAN pagingIrp; if (!IopVerifierOn) { return IopfCallDriver(DeviceObject, Irp); } if (Irp->Type != IO_TYPE_IRP) { KeBugCheckEx(DRIVER_VERIFIER_IOMANAGER_VIOLATION, IO_CALL_DRIVER_IRP_TYPE_INVALID, (ULONG_PTR)Irp, 0, 0); } if (!IovpValidateDeviceObject(DeviceObject)) { KeBugCheckEx(DRIVER_VERIFIER_IOMANAGER_VIOLATION, IO_CALL_DRIVER_INVALID_DEVICE_OBJECT, (ULONG_PTR)DeviceObject, 0, 0); } saveIrql = KeGetCurrentIrql(); // // Deadlock verifier functions are called before and after the // real IoCallDriver() call. If deadlock verifier is not enabled // this functions will return immediately. // pagingIrp = VfDeadlockBeforeCallDriver(DeviceObject, Irp); // // VfIrpCallDriverPreprocess is a macro function that may do an alloca as // part of it's operation. As such callers must be careful not to use // variable lengthed arrays in a scope that encompasses // VfIrpCallDriverPreProcess but not VfIrpCallDriverPostProcess. // VfIrpCallDriverPreProcess(DeviceObject, &Irp, &iofCallDriverStackData); VfStackSeedStack(0xFFFFFFFF); status = IopfCallDriver(DeviceObject, Irp); VfIrpCallDriverPostProcess(DeviceObject, &status, iofCallDriverStackData); VfDeadlockAfterCallDriver(DeviceObject, Irp, pagingIrp); if (saveIrql != KeGetCurrentIrql()) { KeBugCheckEx(DRIVER_VERIFIER_IOMANAGER_VIOLATION, IO_CALL_DRIVER_IRQL_NOT_EQUAL, (ULONG_PTR)DeviceObject, saveIrql, KeGetCurrentIrql()); } return status; } // // Wrapper for IovAllocateIrp. Use special pool to allocate the IRP. // This is directly called from IoAllocateIrp. // PIRP IovAllocateIrp( IN CCHAR StackSize, IN BOOLEAN ChargeQuota ) { USHORT allocateSize; UCHAR fixedSize; PIRP irp; USHORT packetSize; // // Should we override normal lookaside caching so that we may catch // more bugs? // VerifierIoAllocateIrp1(StackSize, ChargeQuota, &irp); if (irp) { return irp; } // // If special pool is not turned on lets just call the standard // irp allocator. // if (!(IovpVerifierFlags & DRIVER_VERIFIER_SPECIAL_POOLING )) { irp = IopAllocateIrpPrivate(StackSize, ChargeQuota); return irp; } irp = NULL; fixedSize = 0; packetSize = IoSizeOfIrp(StackSize); allocateSize = packetSize; // // There are no free packets on the lookaside list, or the packet is // too large to be allocated from one of the lists, so it must be // allocated from nonpaged pool. If quota is to be charged, charge it // against the current process. Otherwise, allocate the pool normally. // if (ChargeQuota) { try { irp = ExAllocatePoolWithTagPriority( NonPagedPool, allocateSize, ' prI', HighPoolPrioritySpecialPoolOverrun); } except(EXCEPTION_EXECUTE_HANDLER) { NOTHING; } } else { // // Attempt to allocate the pool from non-paged pool. If this // fails, and the caller's previous mode was kernel then allocate // the pool as must succeed. // irp = ExAllocatePoolWithTagPriority( NonPagedPool, allocateSize, ' prI', HighPoolPrioritySpecialPoolOverrun); } if (!irp) { return NULL; } // // Initialize the packet. // IopInitializeIrp(irp, packetSize, StackSize); if (ChargeQuota) { irp->AllocationFlags |= IRP_QUOTA_CHARGED; } VerifierIoAllocateIrp2(irp); return irp; } PIRP IovBuildAsynchronousFsdRequest( IN ULONG MajorFunction, IN PDEVICE_OBJECT DeviceObject, IN OUT PVOID Buffer OPTIONAL, IN ULONG Length OPTIONAL, IN PLARGE_INTEGER StartingOffset OPTIONAL, IN PIO_STATUS_BLOCK IoStatusBlock OPTIONAL ) { PIRP Irp; try { Irp = IoBuildAsynchronousFsdRequest( MajorFunction, DeviceObject, Buffer, Length, StartingOffset, IoStatusBlock ); } except(EXCEPTION_EXECUTE_HANDLER) { KeBugCheckEx(DRIVER_VERIFIER_IOMANAGER_VIOLATION, IO_BUILD_FSD_REQUEST_EXCEPTION, (ULONG_PTR)DeviceObject, (ULONG_PTR)MajorFunction, GetExceptionCode()); } return (Irp); } PIRP IovBuildDeviceIoControlRequest( IN ULONG IoControlCode, IN PDEVICE_OBJECT DeviceObject, IN PVOID InputBuffer OPTIONAL, IN ULONG InputBufferLength, OUT PVOID OutputBuffer OPTIONAL, IN ULONG OutputBufferLength, IN BOOLEAN InternalDeviceIoControl, IN PKEVENT Event, OUT PIO_STATUS_BLOCK IoStatusBlock ) { PIRP Irp; try { Irp = IoBuildDeviceIoControlRequest( IoControlCode, DeviceObject, InputBuffer, InputBufferLength, OutputBuffer, OutputBufferLength, InternalDeviceIoControl, Event, IoStatusBlock ); } except(EXCEPTION_EXECUTE_HANDLER) { KeBugCheckEx(DRIVER_VERIFIER_IOMANAGER_VIOLATION, IO_BUILD_IOCTL_REQUEST_EXCEPTION, (ULONG_PTR)DeviceObject, (ULONG_PTR)IoControlCode, GetExceptionCode()); } return (Irp); } NTSTATUS IovInitializeTimer( IN PDEVICE_OBJECT DeviceObject, IN PIO_TIMER_ROUTINE TimerRoutine, IN PVOID Context ) { if (DeviceObject->Timer) { KeBugCheckEx(DRIVER_VERIFIER_IOMANAGER_VIOLATION, IO_REINITIALIZING_TIMER_OBJECT, (ULONG_PTR)DeviceObject, 0, 0); } return (IoInitializeTimer(DeviceObject, TimerRoutine, Context)); } VOID IovpCompleteRequest( IN PKAPC Apc, IN PVOID *SystemArgument1, IN PVOID *SystemArgument2 ) { PIRP irp; PUCHAR addr; ULONG BestStackOffset; irp = CONTAINING_RECORD( Apc, IRP, Tail.Apc ); #if defined(_X86_) addr = (PUCHAR)irp->UserIosb; if ((addr > (PUCHAR)KeGetCurrentThread()->StackLimit) && (addr <= (PUCHAR)&BestStackOffset)) { KeBugCheckEx(DRIVER_VERIFIER_IOMANAGER_VIOLATION, IO_INVALID_STACK_IOSB, (ULONG_PTR)addr, 0, 0); } addr = (PUCHAR)irp->UserEvent; if ((addr > (PUCHAR)KeGetCurrentThread()->StackLimit) && (addr <= (PUCHAR)&BestStackOffset)) { KeBugCheckEx(DRIVER_VERIFIER_IOMANAGER_VIOLATION, IO_INVALID_STACK_EVENT, (ULONG_PTR)addr, 0, 0); } #endif } /*-------------------------- SPECIALIRP HOOKS -------------------------------*/ VOID FASTCALL IovCompleteRequest( IN PIRP Irp, IN CCHAR PriorityBoost ) { ULONG stackContextIndex = 0; IOV_COMPLETION_CONTEXT StackContext; PIOV_COMPLETION_CONTEXT pStackContext; IOFCOMPLETEREQUEST_STACKDATA completionPacket; LONG currentLocation; PIO_STACK_LOCATION stackPointer; if (!IopVerifierOn) { IopfCompleteRequest(Irp, PriorityBoost); return; } if (Irp->CurrentLocation > (CCHAR) (Irp->StackCount + 1) || Irp->Type != IO_TYPE_IRP) { KeBugCheckEx( MULTIPLE_IRP_COMPLETE_REQUESTS, (ULONG_PTR) Irp, __LINE__, 0, 0); } if (Irp->CancelRoutine) { KeBugCheckEx(DRIVER_VERIFIER_IOMANAGER_VIOLATION, IO_COMPLETE_REQUEST_CANCEL_ROUTINE_SET, (ULONG_PTR)Irp->CancelRoutine, (ULONG_PTR)Irp, 0); } if (Irp->IoStatus.Status == STATUS_PENDING || Irp->IoStatus.Status == 0xffffffff) { KeBugCheckEx(DRIVER_VERIFIER_IOMANAGER_VIOLATION, IO_COMPLETE_REQUEST_INVALID_STATUS, Irp->IoStatus.Status, (ULONG_PTR)Irp, 0); } if (KeGetCurrentIrql() > DISPATCH_LEVEL) { KeBugCheckEx(DRIVER_VERIFIER_IOMANAGER_VIOLATION, IO_COMPLETE_REQUEST_INVALID_IRQL, KeGetCurrentIrql(), (ULONG_PTR)Irp, 0); } if (IovpVerifierLevel <= 1) { IopfCompleteRequest(Irp, PriorityBoost); return; } SPECIALIRP_IOF_COMPLETE_1(Irp, PriorityBoost, &completionPacket); if ((Irp->CurrentLocation) > (CCHAR) (Irp->StackCount)) { IopfCompleteRequest(Irp, PriorityBoost); return; } currentLocation = Irp->CurrentLocation; pStackContext = &StackContext; stackPointer = IoGetCurrentIrpStackLocation(Irp); // // Replace the completion routines with verifier completion routines so that // verifier gets control. // IovpKdPrint(("Hook:Irp 0x%x StackCount %d currentlocation %d stackpointer 0%x\n", Irp, Irp->StackCount, currentLocation, IoGetCurrentIrpStackLocation(Irp))); pStackContext->CompletionRoutine = NULL; pStackContext->GlobalContext = &completionPacket; pStackContext->IrpContext = stackPointer->Context; pStackContext->StackPointer = stackPointer; pStackContext->OldStackContents = *(stackPointer); // Save the stack contents IovpKdPrint(("Seeding completion Rtn 0x%x currentLocation %d stackpointer 0x%x pStackContext 0x%x \n", stackPointer->CompletionRoutine, currentLocation, stackPointer, pStackContext )); if ( (NT_SUCCESS( Irp->IoStatus.Status ) && stackPointer->Control & SL_INVOKE_ON_SUCCESS) || (!NT_SUCCESS( Irp->IoStatus.Status ) && stackPointer->Control & SL_INVOKE_ON_ERROR) || (Irp->Cancel && stackPointer->Control & SL_INVOKE_ON_CANCEL) ) { pStackContext->CompletionRoutine = stackPointer->CompletionRoutine; pStackContext->IrpContext = stackPointer->Context; } else { // // Force the completion routine to be called. // Store the old control flag value. // stackPointer->Control |= SL_INVOKE_ON_SUCCESS|SL_INVOKE_ON_ERROR; } stackPointer->CompletionRoutine = IovpLocalCompletionRoutine; stackPointer->Context = pStackContext; IopfCompleteRequest(Irp, PriorityBoost); } #define ZeroAndDopeIrpStackLocation( IrpSp ) { \ (IrpSp)->MinorFunction = 0; \ (IrpSp)->Flags = 0; \ (IrpSp)->Control = SL_NOTCOPIED; \ (IrpSp)->Parameters.Others.Argument1 = 0; \ (IrpSp)->Parameters.Others.Argument2 = 0; \ (IrpSp)->Parameters.Others.Argument3 = 0; \ (IrpSp)->Parameters.Others.Argument4 = 0; \ (IrpSp)->FileObject = (PFILE_OBJECT) NULL; } NTSTATUS IovpLocalCompletionRoutine( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) { PIOV_COMPLETION_CONTEXT pStackContext = Context; NTSTATUS status; PIO_STACK_LOCATION stackPointer = pStackContext->StackPointer; BOOLEAN lastStackLocation = FALSE; // // Copy back all parameters that were zeroed out. // // stackPointer->MinorFunction = pStackContext->OldStackContents.MinorFunction; stackPointer->Flags = pStackContext->OldStackContents.Flags; stackPointer->Control = pStackContext->OldStackContents.Control; stackPointer->Parameters.Others.Argument1 = pStackContext->OldStackContents.Parameters.Others.Argument1; stackPointer->Parameters.Others.Argument2 = pStackContext->OldStackContents.Parameters.Others.Argument2; stackPointer->Parameters.Others.Argument3 = pStackContext->OldStackContents.Parameters.Others.Argument3; stackPointer->Parameters.Others.Argument4 = pStackContext->OldStackContents.Parameters.Others.Argument4; stackPointer->FileObject = pStackContext->OldStackContents.FileObject; // // Put these back too. // stackPointer->CompletionRoutine = pStackContext->CompletionRoutine; stackPointer->Context = pStackContext->IrpContext; // // Get this before the IRP is freed. // lastStackLocation = (Irp->CurrentLocation == (CCHAR) (Irp->StackCount + 1)); // // Simulated completion routine. // SPECIALIRP_IOF_COMPLETE_2(Irp, pStackContext->GlobalContext); ZeroAndDopeIrpStackLocation( stackPointer ); if (!stackPointer->CompletionRoutine) { IovpKdPrint(("Local completion routine null stackpointer 0x%x \n", stackPointer)); // // Handle things as if no completion routine existed. // if (Irp->PendingReturned && Irp->CurrentLocation <= Irp->StackCount) { IoMarkIrpPending( Irp ); } status = STATUS_SUCCESS; } else { IovpKdPrint(("Local completion routine 0x%x stackpointer 0x%x \n", routine, stackPointer)); // // A completion routine exists, call it now. // SPECIALIRP_IOF_COMPLETE_3(Irp, stackPointer->CompletionRoutine, pStackContext->GlobalContext); status = stackPointer->CompletionRoutine(DeviceObject, Irp, stackPointer->Context); SPECIALIRP_IOF_COMPLETE_4(Irp, status, pStackContext->GlobalContext); } SPECIALIRP_IOF_COMPLETE_5(Irp, pStackContext->GlobalContext); if (status != STATUS_MORE_PROCESSING_REQUIRED && !lastStackLocation) { // // Seed the next location. We can touch the stack as the IRP is still valid // stackPointer++; pStackContext->StackPointer = stackPointer; pStackContext->CompletionRoutine = NULL; pStackContext->IrpContext = stackPointer->Context; pStackContext->StackPointer = stackPointer; pStackContext->OldStackContents = *(stackPointer); // Save the stack contents if ( (NT_SUCCESS( Irp->IoStatus.Status ) && stackPointer->Control & SL_INVOKE_ON_SUCCESS) || (!NT_SUCCESS( Irp->IoStatus.Status ) && stackPointer->Control & SL_INVOKE_ON_ERROR) || (Irp->Cancel && stackPointer->Control & SL_INVOKE_ON_CANCEL) ) { pStackContext->CompletionRoutine = stackPointer->CompletionRoutine; pStackContext->IrpContext = stackPointer->Context; } else { // // Force the completion routine to be called. // Store the old control flag value. // stackPointer->Control |= SL_INVOKE_ON_SUCCESS|SL_INVOKE_ON_ERROR; } stackPointer->CompletionRoutine = IovpLocalCompletionRoutine; stackPointer->Context = pStackContext; IovpKdPrint(("Seeding completion Rtn 0x%x currentLocation %d stackpointer 0x%x pStackContext 0x%x \n", stackPointer->CompletionRoutine, Irp->CurrentLocation, stackPointer, pStackContext )); } return status; } VOID IovInitializeIrp( PIRP Irp, USHORT PacketSize, CCHAR StackSize ) { BOOLEAN initializeHandled ; if (IovpVerifierLevel < 2) { return; } VerifierIoInitializeIrp(Irp, PacketSize, StackSize, &initializeHandled); } VOID IovAttachDeviceToDeviceStack( PDEVICE_OBJECT SourceDevice, PDEVICE_OBJECT TargetDevice ) { if (IovpVerifierLevel < 2) { return; } VerifierIoAttachDeviceToDeviceStack(SourceDevice, TargetDevice); } VOID IovDeleteDevice( PDEVICE_OBJECT DeleteDevice ) { if (IovpVerifierFlags & DRIVER_VERIFIER_DMA_VERIFIER) { VfHalDeleteDevice(DeleteDevice); } if (IovpVerifierLevel < 2) { return; } VerifierIoDeleteDevice(DeleteDevice); } VOID IovDetachDevice( PDEVICE_OBJECT TargetDevice ) { if (IovpVerifierLevel < 2) { return; } VerifierIoDetachDevice(TargetDevice); } BOOLEAN IovCancelIrp( PIRP Irp, BOOLEAN *returnValue ) { BOOLEAN cancelHandled; SPECIALIRP_IO_CANCEL_IRP(Irp, &cancelHandled, returnValue) ; return cancelHandled; } typedef struct _IOV_DRIVER_LIST_ENTRY { SINGLE_LIST_ENTRY listEntry; PDRIVER_OBJECT DriverObject; } IOV_DRIVER_LIST_ENTRY, *PIOV_DRIVER_LIST_ENTRY; SINGLE_LIST_ENTRY IovDriverListHead; BOOLEAN IovpBuildDriverObjectList( IN PVOID Object, IN PUNICODE_STRING ObjectName, IN ULONG HandleCount, IN ULONG PointerCount, IN PVOID Parameter ) { PIOV_DRIVER_LIST_ENTRY driverListEntry; PDRIVER_OBJECT driverObject; driverObject = (PDRIVER_OBJECT)Object; if (IopIsLegacyDriver(driverObject)) { driverListEntry = ExAllocatePoolWithTag( NonPagedPool, sizeof(IOV_DRIVER_LIST_ENTRY), 'ovI' ); if (!driverListEntry) { return FALSE; } if (ObReferenceObjectSafe(driverObject)) { driverListEntry->DriverObject = driverObject; PushEntryList(&IovDriverListHead, &driverListEntry->listEntry); } else { ExFreePool (driverListEntry); } } else { IovpKdPrint (("Rejected non-legacy driver %wZ (%p)\n", &driverObject->DriverName, driverObject)); } return TRUE; } NTSTATUS IovpUnloadDriver( PDRIVER_OBJECT DriverObject ) { NTSTATUS status; BOOLEAN unloadDriver; // // Check to see whether or not this driver implements unload. // if (DriverObject->DriverUnload == (PDRIVER_UNLOAD) NULL) { IovpKdPrint (("No unload routine for driver %wZ (%p)\n", &DriverObject->DriverName, DriverObject)); return STATUS_INVALID_DEVICE_REQUEST; } // // Check to see whether the driver has already been marked for an unload // operation by anyone in the past. // ObReferenceObject (DriverObject); status = IopCheckUnloadDriver(DriverObject,&unloadDriver); if ( NT_SUCCESS(status) ) { return STATUS_PENDING; } ObDereferenceObject (DriverObject); if (unloadDriver) { // // If the current thread is not executing in the context of the system // process, which is required in order to invoke the driver's unload // routine. Queue a worker item to one of the worker threads to // get into the appropriate process context and then invoke the // routine. // if (PsGetCurrentProcess() == PsInitialSystemProcess) { // // The current thread is alrady executing in the context of the // system process, so simply invoke the driver's unload routine. // IovpKdPrint (("Calling unload for driver %wZ (%p)\n", &DriverObject->DriverName, DriverObject)); DriverObject->DriverUnload( DriverObject ); IovpKdPrint (("Unload returned for driver %wZ (%p)\n", &DriverObject->DriverName, DriverObject)); } else { LOAD_PACKET loadPacket; KeInitializeEvent( &loadPacket.Event, NotificationEvent, FALSE ); loadPacket.DriverObject = DriverObject; ExInitializeWorkItem( &loadPacket.WorkQueueItem, IopLoadUnloadDriver, &loadPacket ); ExQueueWorkItem( &loadPacket.WorkQueueItem, DelayedWorkQueue ); (VOID) KeWaitForSingleObject( &loadPacket.Event, Executive, KernelMode, FALSE, (PLARGE_INTEGER) NULL ); } ObMakeTemporaryObject( DriverObject ); ObDereferenceObject( DriverObject ); return STATUS_SUCCESS; } else { return STATUS_PENDING; } } NTSTATUS IovUnloadDrivers( VOID ) { NTSTATUS status; PSINGLE_LIST_ENTRY listEntry; PIOV_DRIVER_LIST_ENTRY driverListEntry; SINGLE_LIST_ENTRY NonUnloadedDrivers, NonUnloadedDriversTmp; BOOLEAN DoneSomething, NeedWait, Break; if (!PoCleanShutdownEnabled ()) return STATUS_UNSUCCESSFUL; IovDriverListHead.Next = NULL; NonUnloadedDrivers.Next = NULL; // // Prepare a list of all driver objects. // status = ObEnumerateObjectsByType( IoDriverObjectType, IovpBuildDriverObjectList, NULL ); // // Walk through the list and unload each driver. // while (TRUE) { listEntry = PopEntryList(&IovDriverListHead); if (listEntry == NULL) { break; } driverListEntry = CONTAINING_RECORD(listEntry, IOV_DRIVER_LIST_ENTRY, listEntry); IovpKdPrint (("Trying to unload %wZ (%p)\n", &driverListEntry->DriverObject->DriverName, driverListEntry->DriverObject)); if (IovpUnloadDriver(driverListEntry->DriverObject) != STATUS_PENDING) { ObDereferenceObject(driverListEntry->DriverObject); ExFreePool(driverListEntry); } else { IovpKdPrint (("Unload of driver %wZ (%p) pended\n", &driverListEntry->DriverObject->DriverName, driverListEntry->DriverObject)); PushEntryList(&NonUnloadedDrivers, &driverListEntry->listEntry); } } // // Walk the drivers that didn't unload straight away and see if any have had their unloads called yet // do { NeedWait = DoneSomething = FALSE; NonUnloadedDriversTmp.Next = NULL; while (TRUE) { listEntry = PopEntryList(&NonUnloadedDrivers); if (listEntry == NULL) { break; } driverListEntry = CONTAINING_RECORD(listEntry, IOV_DRIVER_LIST_ENTRY, listEntry); // // If driver unload is queued to be invoked then // if (driverListEntry->DriverObject->Flags & DRVO_UNLOAD_INVOKED) { IovpKdPrint (("Pending unload of driver %wZ (%p) is being invoked\n", &driverListEntry->DriverObject->DriverName, driverListEntry->DriverObject)); ObDereferenceObject(driverListEntry->DriverObject); ExFreePool(driverListEntry); NeedWait = TRUE; } else { PushEntryList(&NonUnloadedDriversTmp, &driverListEntry->listEntry); } } if (NeedWait) { LARGE_INTEGER tmo = {(ULONG)(-10 * 1000 * 1000 * 10), -1}; ZwDelayExecution (FALSE, &tmo); DoneSomething = TRUE; } NonUnloadedDrivers = NonUnloadedDriversTmp; } while (DoneSomething == TRUE && NonUnloadedDrivers.Next != NULL); // // All the drivers left didn't have unload called becuase they had files open etc // Break = FALSE; while (TRUE) { listEntry = PopEntryList(&NonUnloadedDrivers); if (listEntry == NULL) { break; } driverListEntry = CONTAINING_RECORD(listEntry, IOV_DRIVER_LIST_ENTRY, listEntry); IovpKdPrint (("Unload never got called for driver %wZ (%p)\n", &driverListEntry->DriverObject->DriverName, driverListEntry->DriverObject)); ObDereferenceObject(driverListEntry->DriverObject); ExFreePool(driverListEntry); Break = TRUE; } if (Break == TRUE) { // DbgBreakPoint (); } return status; }