1112 lines
30 KiB
C
1112 lines
30 KiB
C
|
/*++
|
||
|
|
||
|
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;
|
||
|
}
|