Accessing User Buffers in a Postoperation Callback Routine
A minifilter driver postoperation callback routine should treat a buffer in an IRP-based I/O operation as follows:
Check whether an MDL exists for the buffer. The MDL pointer can be found in the MdlAddress or OutputMdlAddress parameter in the FLT_PARAMETERS for the operation. Minifilter drivers can call FltDecodeParameters to query for the MDL pointer.
One method for obtaining a valid MDL is to look for the IRP_MN_MDL flag in the MinorFunction member of the I/O parameter block, FLT_IO_PARAMETER_BLOCK, in the callback data. The following example shows how to check for the IRP_MN_MDL flag.
NTSTATUS status; PMDL *ReadMdl = NULL; PVOID ReadAddress = NULL; if (FlagOn(CallbackData->Iopb->MinorFunction, IRP_MN_MDL)) { ReadMdl = &CallbackData->Iopb->Parameters.Read.MdlAddress; }
However, the IRP_MN_MDL flag can be set only for read and write operations. It is best to use FltDecodeParameters to retrieve an MDL, because the routine checks for a valid MDL for any operation. In the following example, only the MDL parameter is returned if valid.
NTSTATUS status; PMDL *ReadMdl = NULL; PVOID ReadAddress = NULL; status = FltDecodeParameters(CallbackData, &ReadMdl, NULL, NULL, NULL);
If an MDL exists for the buffer, call MmGetSystemAddressForMdlSafe to obtain the system address for the buffer and then use this address to access the buffer. (MmGetSystemAddressForMdlSafe can be called at IRQL <= DISPATCH_LEVEL.)
Continuing from the previous example, the following code obtains the system address.
if (*ReadMdl != NULL) { ReadAddress = MmGetSystemAddressForMdlSafe(*ReadMdl, NormalPagePriority); if (ReadAddress == NULL) { CallbackData->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; CallbackData->IoStatus.Information = 0; } }
If there is no MDL for the buffer, check whether the system buffer flag is set for the operation by using the FLT_IS_SYSTEM_BUFFER macro.
If the FLT_IS_SYSTEM_BUFFER macro returns TRUE, the operation uses buffered I/O, and the buffer can safely be accessed at IRQL = DISPATCH_LEVEL.
If the FLT_IS_SYSTEM_BUFFER macro returns FALSE, the buffer cannot safely be accessed at IRQL = DISPATCH_LEVEL. If the postoperation callback routine can be called at DISPATCH_LEVEL, it must call FltDoCompletionProcessingWhenSafe to pend the operation until it can be processed at IRQL <= APC_LEVEL. The callback routine that is pointed to by the SafePostCallback parameter of FltDoCompletionProcessingWhenSafe should first call FltLockUserBuffer to lock the buffer and then call MmGetSystemAddressForMdlSafe to obtain the system address for the buffer.
A postoperation callback routine should treat a buffer in a fast I/O operation as follows:
Use the buffer address to access the buffer (because a fast I/O operation cannot have an MDL).
To ensure that a user-space buffer address is valid, the minifilter driver must use a routine such as ProbeForRead or ProbeForWrite, enclosing all buffer references in try/except blocks.
The postoperation callback routine for a fast I/O operation is guaranteed to be called in the correct thread context.
The postoperation callback routine for a fast I/O operation is guaranteed to be called at IRQL <= APC_LEVEL, so it can safely call routines such as FltLockUserBuffer.
The following example code fragment checks for the system buffer or fast I/O flags for a directory control operation and defers completion processing if necessary.
if (*DirectoryControlMdl == NULL)
{
if (FLT_IS_SYSTEM_BUFFER(CallbackData) || FLT_IS_FASTIO_OPERATION(CallbackData))
{
dirBuffer = CallbackData->Iopb->Parameters.DirectoryControl.QueryDirectory.DirectoryBuffer;
}
else
{
// Defer processing until safe.
if (!FltDoCompletionProcessingWhenSafe(CallbackData, FltObjects, CompletionContext, Flags, ProcessPostDirCtrlWhenSafe, &retValue))
{
CallbackData->IoStatus.Status = STATUS_UNSUCCESSFUL;
CallbackData->IoStatus.Information = 0;
}
}
}
For operations that can be either fast I/O or IRP-based, all buffer references should be enclosed in try/except blocks. Although you do not have to enclose these references for IRP-based operations that use buffered I/O, the try/except blocks are a safe precaution.