617 lines
16 KiB
C
617 lines
16 KiB
C
/*++
|
|
|
|
Copyright (c) 2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
vfgeneric.c
|
|
|
|
Abstract:
|
|
|
|
This module handles generic Irp verification.
|
|
|
|
Author:
|
|
|
|
Adrian J. Oney (adriao) 20-Apr-1998
|
|
|
|
Environment:
|
|
|
|
Kernel mode
|
|
|
|
Revision History:
|
|
|
|
AdriaO 06/15/2000 - Seperated out from ntos\io\flunkirp.c
|
|
|
|
--*/
|
|
|
|
#include "vfdef.h"
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(INIT, VfGenericInit)
|
|
#pragma alloc_text(PAGEVRFY, VfGenericDumpIrpStack)
|
|
#pragma alloc_text(PAGEVRFY, VfGenericVerifyNewRequest)
|
|
#pragma alloc_text(PAGEVRFY, VfGenericVerifyIrpStackDownward)
|
|
#pragma alloc_text(PAGEVRFY, VfGenericVerifyIrpStackUpward)
|
|
#pragma alloc_text(PAGEVRFY, VfGenericIsValidIrpStatus)
|
|
#pragma alloc_text(PAGEVRFY, VfGenericIsNewRequest)
|
|
#pragma alloc_text(PAGEVRFY, VfGenericVerifyNewIrp)
|
|
#pragma alloc_text(PAGEVRFY, VfGenericVerifyFinalIrpStack)
|
|
#endif
|
|
|
|
#ifdef ALLOC_DATA_PRAGMA
|
|
#pragma const_seg("PAGEVRFC")
|
|
#endif
|
|
|
|
const PCHAR IrpMajorNames[] = {
|
|
"IRP_MJ_CREATE", // 0x00
|
|
"IRP_MJ_CREATE_NAMED_PIPE", // 0x01
|
|
"IRP_MJ_CLOSE", // 0x02
|
|
"IRP_MJ_READ", // 0x03
|
|
"IRP_MJ_WRITE", // 0x04
|
|
"IRP_MJ_QUERY_INFORMATION", // 0x05
|
|
"IRP_MJ_SET_INFORMATION", // 0x06
|
|
"IRP_MJ_QUERY_EA", // 0x07
|
|
"IRP_MJ_SET_EA", // 0x08
|
|
"IRP_MJ_FLUSH_BUFFERS", // 0x09
|
|
"IRP_MJ_QUERY_VOLUME_INFORMATION", // 0x0a
|
|
"IRP_MJ_SET_VOLUME_INFORMATION", // 0x0b
|
|
"IRP_MJ_DIRECTORY_CONTROL", // 0x0c
|
|
"IRP_MJ_FILE_SYSTEM_CONTROL", // 0x0d
|
|
"IRP_MJ_DEVICE_CONTROL", // 0x0e
|
|
"IRP_MJ_INTERNAL_DEVICE_CONTROL", // 0x0f
|
|
"IRP_MJ_SHUTDOWN", // 0x10
|
|
"IRP_MJ_LOCK_CONTROL", // 0x11
|
|
"IRP_MJ_CLEANUP", // 0x12
|
|
"IRP_MJ_CREATE_MAILSLOT", // 0x13
|
|
"IRP_MJ_QUERY_SECURITY", // 0x14
|
|
"IRP_MJ_SET_SECURITY", // 0x15
|
|
"IRP_MJ_POWER", // 0x16
|
|
"IRP_MJ_SYSTEM_CONTROL", // 0x17
|
|
"IRP_MJ_DEVICE_CHANGE", // 0x18
|
|
"IRP_MJ_QUERY_QUOTA", // 0x19
|
|
"IRP_MJ_SET_QUOTA", // 0x1a
|
|
"IRP_MJ_PNP", // 0x1b
|
|
NULL
|
|
};
|
|
|
|
#define MAX_NAMED_MAJOR_IRPS 0x1b
|
|
|
|
#ifdef ALLOC_DATA_PRAGMA
|
|
#pragma const_seg()
|
|
#endif // ALLOC_DATA_PRAGMA
|
|
|
|
|
|
VOID
|
|
VfGenericInit(
|
|
VOID
|
|
)
|
|
{
|
|
VfMajorRegisterHandlers(
|
|
IRP_MJ_ALL_MAJORS,
|
|
VfGenericDumpIrpStack,
|
|
VfGenericVerifyNewRequest,
|
|
VfGenericVerifyIrpStackDownward,
|
|
VfGenericVerifyIrpStackUpward,
|
|
NULL,
|
|
NULL,
|
|
VfGenericIsValidIrpStatus,
|
|
VfGenericIsNewRequest,
|
|
VfGenericVerifyNewIrp,
|
|
VfGenericVerifyFinalIrpStack,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
|
|
VOID
|
|
FASTCALL
|
|
VfGenericDumpIrpStack(
|
|
IN PIO_STACK_LOCATION IrpSp
|
|
)
|
|
{
|
|
if ((IrpSp->MajorFunction==IRP_MJ_INTERNAL_DEVICE_CONTROL)&&(IrpSp->MinorFunction == IRP_MN_SCSI_CLASS)) {
|
|
|
|
DbgPrint("IRP_MJ_SCSI");
|
|
|
|
} else if (IrpSp->MajorFunction<=MAX_NAMED_MAJOR_IRPS) {
|
|
|
|
DbgPrint(IrpMajorNames[IrpSp->MajorFunction]);
|
|
|
|
} else if (IrpSp->MajorFunction==0xFF) {
|
|
|
|
DbgPrint("IRP_MJ_BOGUS");
|
|
|
|
} else {
|
|
|
|
DbgPrint("IRP_MJ_??");
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
FASTCALL
|
|
VfGenericVerifyNewRequest(
|
|
IN PIOV_REQUEST_PACKET IovPacket,
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIO_STACK_LOCATION IrpLastSp OPTIONAL,
|
|
IN PIO_STACK_LOCATION IrpSp,
|
|
IN PIOV_STACK_LOCATION StackLocationData,
|
|
IN PVOID CallerAddress OPTIONAL
|
|
)
|
|
{
|
|
UNREFERENCED_PARAMETER (DeviceObject);
|
|
UNREFERENCED_PARAMETER (IrpLastSp);
|
|
UNREFERENCED_PARAMETER (StackLocationData);
|
|
|
|
if (!VfSettingsIsOptionEnabled(IovPacket->VerifierSettings, VERIFIER_OPTION_PROTECT_RESERVED_IRPS)) {
|
|
|
|
return;
|
|
}
|
|
|
|
if ((IovPacket->Flags&TRACKFLAG_IO_ALLOCATED)&&
|
|
(!(IovPacket->Flags&TRACKFLAG_WATERMARKED))) {
|
|
|
|
if (VfMajorIsSystemRestrictedIrp(IrpSp)) {
|
|
|
|
//
|
|
// We've caught somebody initiating an IRP they shouldn't be sending!
|
|
//
|
|
WDM_FAIL_ROUTINE((
|
|
DCERROR_RESTRICTED_IRP,
|
|
DCPARAM_IRP + DCPARAM_ROUTINE,
|
|
CallerAddress,
|
|
IovPacket->TrackedIrp
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
FASTCALL
|
|
VfGenericVerifyIrpStackDownward(
|
|
IN PIOV_REQUEST_PACKET IovPacket,
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIO_STACK_LOCATION IrpLastSp OPTIONAL,
|
|
IN PIO_STACK_LOCATION IrpSp,
|
|
IN PIOV_STACK_LOCATION RequestHeadLocationData,
|
|
IN PIOV_STACK_LOCATION StackLocationData,
|
|
IN PVOID CallerAddress OPTIONAL
|
|
)
|
|
{
|
|
PIRP irp = IovPacket->TrackedIrp;
|
|
NTSTATUS currentStatus, lastStatus;
|
|
BOOLEAN newRequest, statusChanged, infoChanged, firstRequest;
|
|
PIOV_SESSION_DATA iovSessionData;
|
|
|
|
UNREFERENCED_PARAMETER (DeviceObject);
|
|
UNREFERENCED_PARAMETER (StackLocationData);
|
|
|
|
|
|
currentStatus = irp->IoStatus.Status;
|
|
lastStatus = RequestHeadLocationData->LastStatusBlock.Status;
|
|
statusChanged = (BOOLEAN)(currentStatus != lastStatus);
|
|
infoChanged = (BOOLEAN)(irp->IoStatus.Information != RequestHeadLocationData->LastStatusBlock.Information);
|
|
firstRequest = (BOOLEAN)((RequestHeadLocationData->Flags&STACKFLAG_FIRST_REQUEST) != 0);
|
|
iovSessionData = VfPacketGetCurrentSessionData(IovPacket);
|
|
|
|
//
|
|
// Do we have a "new" function to process?
|
|
//
|
|
newRequest = VfMajorIsNewRequest(IrpLastSp, IrpSp);
|
|
|
|
//
|
|
// Verify IRQL's are legal
|
|
//
|
|
switch(IrpSp->MajorFunction) {
|
|
|
|
case IRP_MJ_POWER:
|
|
case IRP_MJ_READ:
|
|
case IRP_MJ_WRITE:
|
|
case IRP_MJ_DEVICE_CONTROL:
|
|
case IRP_MJ_INTERNAL_DEVICE_CONTROL:
|
|
break;
|
|
default:
|
|
if (iovSessionData->ForwardMethod != FORWARDED_TO_NEXT_DO) {
|
|
break;
|
|
}
|
|
|
|
if ((IovPacket->DepartureIrql >= DISPATCH_LEVEL) &&
|
|
(!(IovPacket->Flags & TRACKFLAG_PASSED_AT_BAD_IRQL))) {
|
|
|
|
WDM_FAIL_ROUTINE((
|
|
DCERROR_DISPATCH_CALLED_AT_BAD_IRQL,
|
|
DCPARAM_IRP + DCPARAM_ROUTINE,
|
|
CallerAddress,
|
|
irp
|
|
));
|
|
|
|
IovPacket->Flags |= TRACKFLAG_PASSED_AT_BAD_IRQL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// The following is only executed if we are not a new IRP...
|
|
//
|
|
if (IrpLastSp == NULL) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Let's verify bogus IRPs haven't been touched...
|
|
//
|
|
if ((IovPacket->Flags&TRACKFLAG_BOGUS) &&
|
|
(!(RequestHeadLocationData->Flags&STACKFLAG_BOGUS_IRP_TOUCHED))) {
|
|
|
|
if (newRequest && (!firstRequest)) {
|
|
|
|
RequestHeadLocationData->Flags |= STACKFLAG_BOGUS_IRP_TOUCHED;
|
|
|
|
WDM_FAIL_ROUTINE((
|
|
DCERROR_BOGUS_FUNC_TRASHED,
|
|
DCPARAM_IRP + DCPARAM_ROUTINE,
|
|
CallerAddress,
|
|
irp
|
|
));
|
|
}
|
|
|
|
if (statusChanged) {
|
|
|
|
RequestHeadLocationData->Flags |= STACKFLAG_BOGUS_IRP_TOUCHED;
|
|
|
|
if (IrpSp->MinorFunction == 0xFF) {
|
|
|
|
WDM_FAIL_ROUTINE((
|
|
DCERROR_BOGUS_MINOR_STATUS_TRASHED,
|
|
DCPARAM_IRP + DCPARAM_ROUTINE,
|
|
CallerAddress,
|
|
irp
|
|
));
|
|
|
|
} else {
|
|
|
|
WDM_FAIL_ROUTINE((
|
|
DCERROR_BOGUS_STATUS_TRASHED,
|
|
DCPARAM_IRP + DCPARAM_ROUTINE,
|
|
CallerAddress,
|
|
irp
|
|
));
|
|
}
|
|
}
|
|
|
|
if (infoChanged) {
|
|
|
|
RequestHeadLocationData->Flags |= STACKFLAG_BOGUS_IRP_TOUCHED;
|
|
|
|
WDM_FAIL_ROUTINE((
|
|
DCERROR_BOGUS_INFO_TRASHED,
|
|
DCPARAM_IRP + DCPARAM_ROUTINE,
|
|
CallerAddress,
|
|
irp
|
|
));
|
|
}
|
|
}
|
|
|
|
if (!VfMajorIsValidIrpStatus(IrpSp, currentStatus)) {
|
|
|
|
WDM_FAIL_ROUTINE((
|
|
DCERROR_INVALID_STATUS,
|
|
DCPARAM_IRP + DCPARAM_ROUTINE,
|
|
CallerAddress,
|
|
irp
|
|
));
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
FASTCALL
|
|
VfGenericVerifyIrpStackUpward(
|
|
IN PIOV_REQUEST_PACKET IovPacket,
|
|
IN PIO_STACK_LOCATION IrpSp,
|
|
IN PIOV_STACK_LOCATION RequestHeadLocationData,
|
|
IN PIOV_STACK_LOCATION StackLocationData,
|
|
IN BOOLEAN IsNewlyCompleted,
|
|
IN BOOLEAN RequestFinalized
|
|
)
|
|
{
|
|
PIRP irp;
|
|
NTSTATUS currentStatus;
|
|
BOOLEAN statusChanged, infoChanged;
|
|
PVOID routine;
|
|
|
|
UNREFERENCED_PARAMETER (IsNewlyCompleted);
|
|
UNREFERENCED_PARAMETER (RequestFinalized);
|
|
|
|
irp = IovPacket->TrackedIrp;
|
|
currentStatus = irp->IoStatus.Status;
|
|
|
|
//
|
|
// Who'd we call for this one?
|
|
//
|
|
routine = StackLocationData->LastDispatch;
|
|
ASSERT(routine) ;
|
|
|
|
//
|
|
// Did they touch something stupid?
|
|
//
|
|
if ((IovPacket->Flags&TRACKFLAG_BOGUS) &&
|
|
(!(RequestHeadLocationData->Flags&STACKFLAG_BOGUS_IRP_TOUCHED))) {
|
|
|
|
statusChanged = (BOOLEAN)(currentStatus != RequestHeadLocationData->LastStatusBlock.Status);
|
|
|
|
if (statusChanged) {
|
|
|
|
RequestHeadLocationData->Flags |= STACKFLAG_BOGUS_IRP_TOUCHED;
|
|
|
|
if (IrpSp->MinorFunction == 0xFF) {
|
|
|
|
WDM_FAIL_ROUTINE((
|
|
DCERROR_BOGUS_MINOR_STATUS_TRASHED,
|
|
DCPARAM_IRP + DCPARAM_ROUTINE,
|
|
routine,
|
|
irp
|
|
));
|
|
|
|
} else {
|
|
|
|
WDM_FAIL_ROUTINE((
|
|
DCERROR_BOGUS_STATUS_TRASHED,
|
|
DCPARAM_IRP + DCPARAM_ROUTINE,
|
|
routine,
|
|
irp
|
|
));
|
|
}
|
|
}
|
|
|
|
infoChanged = (BOOLEAN)(irp->IoStatus.Information != RequestHeadLocationData->LastStatusBlock.Information);
|
|
if (infoChanged) {
|
|
|
|
RequestHeadLocationData->Flags |= STACKFLAG_BOGUS_IRP_TOUCHED;
|
|
|
|
WDM_FAIL_ROUTINE((
|
|
DCERROR_BOGUS_INFO_TRASHED,
|
|
DCPARAM_IRP + DCPARAM_ROUTINE,
|
|
routine,
|
|
irp
|
|
));
|
|
}
|
|
}
|
|
|
|
if (!VfMajorIsValidIrpStatus(IrpSp, currentStatus)) {
|
|
|
|
WDM_FAIL_ROUTINE(
|
|
(DCERROR_INVALID_STATUS, DCPARAM_IRP + DCPARAM_ROUTINE, routine, irp)
|
|
);
|
|
}
|
|
|
|
//
|
|
// Check for leaked Cancel routines.
|
|
//
|
|
if (irp->CancelRoutine) {
|
|
|
|
if (VfSettingsIsOptionEnabled(IovPacket->VerifierSettings, VERIFIER_OPTION_VERIFY_CANCEL_LOGIC)) {
|
|
|
|
WDM_FAIL_ROUTINE((
|
|
DCERROR_CANCELROUTINE_AFTER_COMPLETION,
|
|
DCPARAM_IRP + DCPARAM_ROUTINE,
|
|
routine,
|
|
irp
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
FASTCALL
|
|
VfGenericIsValidIrpStatus(
|
|
IN PIO_STACK_LOCATION IrpSp,
|
|
IN NTSTATUS Status
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
As per the title, this function determines whether an IRP status is
|
|
valid or probably random trash. See NTStatus.h for info on how status
|
|
codes break down...
|
|
|
|
Returns:
|
|
|
|
TRUE iff IRP status looks to be valid. FALSE otherwise.
|
|
--*/
|
|
{
|
|
ULONG severity;
|
|
ULONG customer;
|
|
ULONG reserved;
|
|
ULONG facility;
|
|
ULONG code;
|
|
ULONG lanManClass;
|
|
|
|
UNREFERENCED_PARAMETER (IrpSp);
|
|
|
|
severity = (((ULONG)Status) >> 30)&3;
|
|
customer = (((ULONG)Status) >> 29)&1;
|
|
reserved = (((ULONG)Status) >> 28)&1;
|
|
facility = (((ULONG)Status) >> 16)&0xFFF;
|
|
code = (((ULONG)Status) & 0xFFFF);
|
|
|
|
//
|
|
// If reserved set, definitely bogus...
|
|
//
|
|
if (reserved) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Is this a microsoft defined return code? If not, do no checking.
|
|
//
|
|
if (customer) {
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// ADRIAO N.B. 10/04/1999 -
|
|
// The current methodology for doling out error codes appears to be
|
|
// fairly chaotic. The primary kernel mode status codes are defined in
|
|
// ntstatus.h. However, rtl\generr.c should also be consulted to see which
|
|
// error codes can bubble up to user mode. Many OLE error codes from
|
|
// winerror.h are now being used within the kernel itself.
|
|
//
|
|
if (facility < 0x20) {
|
|
|
|
//
|
|
// Facilities under 20 are currently legal.
|
|
//
|
|
switch(severity) {
|
|
case STATUS_SEVERITY_SUCCESS: return (BOOLEAN)(code < 0x200);
|
|
case STATUS_SEVERITY_INFORMATIONAL:
|
|
//
|
|
// ADRIAO N.B. 06/27/2000
|
|
// This test could be tighter (a little over 0x50)
|
|
//
|
|
return (BOOLEAN)(code < 0x400);
|
|
case STATUS_SEVERITY_WARNING: return (BOOLEAN)(code < 0x400);
|
|
case STATUS_SEVERITY_ERROR: break;
|
|
}
|
|
|
|
//
|
|
// Why the heck does WOW use such an odd error code?
|
|
//
|
|
return (BOOLEAN)((code < 0x400)||(code == 0x9898));
|
|
|
|
} else if (facility == 0x98) {
|
|
|
|
//
|
|
// This is the lan manager service. In the case on Lan Man, the code
|
|
// field is further subdivided into a class field.
|
|
//
|
|
lanManClass = code >> 12;
|
|
code &= 0xFFF;
|
|
|
|
//
|
|
// Do no testing here.
|
|
//
|
|
return TRUE;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Not known, probably bogus.
|
|
//
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
FASTCALL
|
|
VfGenericIsNewRequest(
|
|
IN PIO_STACK_LOCATION IrpLastSp OPTIONAL,
|
|
IN PIO_STACK_LOCATION IrpSp
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Determines whether the two Irp stacks refer to the same "request",
|
|
ie starting the same device, etc. This is used to detect whether an IRP
|
|
has been simply forwarded or rather the IRP has been reused to initiate
|
|
a new request.
|
|
|
|
Arguments:
|
|
|
|
The two IRP stacks to compare.
|
|
|
|
N.B. - the device object is not currently part of those IRP stacks.
|
|
|
|
Return Value:
|
|
|
|
TRUE if the stacks represent the same request, FALSE otherwise.
|
|
|
|
--*/
|
|
{
|
|
return (BOOLEAN)((IrpLastSp==NULL)||
|
|
(IrpSp->MajorFunction != IrpLastSp->MajorFunction) ||
|
|
(IrpSp->MinorFunction != IrpLastSp->MinorFunction));
|
|
}
|
|
|
|
|
|
VOID
|
|
FASTCALL
|
|
VfGenericVerifyNewIrp(
|
|
IN PIOV_REQUEST_PACKET IovPacket,
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp,
|
|
IN PIOV_STACK_LOCATION StackLocationData,
|
|
IN PVOID CallerAddress OPTIONAL
|
|
)
|
|
{
|
|
LONG index;
|
|
PIO_STACK_LOCATION irpSp;
|
|
BOOLEAN queuesApc;
|
|
|
|
UNREFERENCED_PARAMETER (IrpSp);
|
|
UNREFERENCED_PARAMETER (StackLocationData);
|
|
|
|
if (Irp->UserIosb || Irp->UserEvent) {
|
|
|
|
//
|
|
// We have an IRP with user buffer data. This kind of IRP must be
|
|
// initiated at PASSIVE_LEVEL lest the APC that signals the event gets
|
|
// held up by fast mutex.
|
|
//
|
|
queuesApc = (BOOLEAN)
|
|
(!((Irp->Flags & (IRP_PAGING_IO | IRP_CLOSE_OPERATION)) &&
|
|
(Irp->Flags & (IRP_SYNCHRONOUS_PAGING_IO | IRP_CLOSE_OPERATION))));
|
|
|
|
if (queuesApc) {
|
|
|
|
//
|
|
// The caller may be using the UserIosb for storage, and may really
|
|
// free the IRP in a completion routine. Look for one now.
|
|
//
|
|
irpSp = IoGetNextIrpStackLocation(Irp);
|
|
for(index = Irp->CurrentLocation-1;
|
|
index <= Irp->StackCount;
|
|
index++) {
|
|
|
|
if (irpSp->CompletionRoutine != NULL) {
|
|
|
|
queuesApc = FALSE;
|
|
break;
|
|
}
|
|
irpSp++;
|
|
}
|
|
}
|
|
|
|
if (queuesApc && (IovPacket->DepartureIrql > PASSIVE_LEVEL)) {
|
|
|
|
WDM_FAIL_ROUTINE((
|
|
DCERROR_DISPATCH_CALLED_AT_BAD_IRQL,
|
|
DCPARAM_IRP + DCPARAM_ROUTINE,
|
|
CallerAddress,
|
|
Irp
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
FASTCALL
|
|
VfGenericVerifyFinalIrpStack(
|
|
IN PIOV_REQUEST_PACKET IovPacket,
|
|
IN PIO_STACK_LOCATION IrpSp
|
|
)
|
|
{
|
|
UNREFERENCED_PARAMETER (IovPacket);
|
|
UNREFERENCED_PARAMETER (IrpSp);
|
|
|
|
ASSERT(!IovPacket->RefTrackingCount);
|
|
}
|
|
|
|
|