/*++ 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); }