访问后操作回调例程中的用户缓冲区
微筛选器驱动程序 操作后回调例程 应在基于 IRP 的 I/O 操作中处理缓冲区,如下所示:
检查缓冲区是否存在 MDL。 MDL 指针可以在操作FLT_PARAMETERS的 MdlAddress 或 OutputMdlAddress 参数中找到。 微筛选器驱动程序可以调用 FltDecodeParameters 来查询 MDL 指针。
获取有效 MDL 的一种方法是在回调数据FLT_IO_PARAMETER_BLOCK I/O 参数块的 MinorFunction 成员中查找 IRP_MN_MDL标志。 以下示例演示如何为 IRP_MN_MDL 标志检查。
NTSTATUS status; PMDL *ReadMdl = NULL; PVOID ReadAddress = NULL; if (FlagOn(CallbackData->Iopb->MinorFunction, IRP_MN_MDL)) { ReadMdl = &CallbackData->Iopb->Parameters.Read.MdlAddress; }
但是,只能为读取和写入操作设置IRP_MN_MDL标志。 最好使用 FltDecodeParameters 检索 MDL,因为例程会检查任何操作的有效 MDL。 在以下示例中,如果有效,则仅返回 MDL 参数。
NTSTATUS status; PMDL *ReadMdl = NULL; PVOID ReadAddress = NULL; status = FltDecodeParameters(CallbackData, &ReadMdl, NULL, NULL, NULL);
如果缓冲区存在 MDL,请调用 MmGetSystemAddressForMdlSafe 以获取缓冲区的系统地址,然后使用此地址访问缓冲区。 (MmGetSystemAddressForMdlSafe 可在 IRQL <= DISPATCH_LEVEL.)
继续上一个示例,以下代码获取系统地址。
if (*ReadMdl != NULL) { ReadAddress = MmGetSystemAddressForMdlSafe(*ReadMdl, NormalPagePriority); if (ReadAddress == NULL) { CallbackData->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; CallbackData->IoStatus.Information = 0; } }
如果缓冲区没有 MDL,检查是否使用 FLT_IS_SYSTEM_BUFFER 宏为操作设置系统缓冲区标志。
如果FLT_IS_SYSTEM_BUFFER宏返回 TRUE,则操作使用缓冲 I/O,并且可以在 IRQL = DISPATCH_LEVEL 安全地访问缓冲区。
如果FLT_IS_SYSTEM_BUFFER宏返回 FALSE,则无法在 IRQL = DISPATCH_LEVEL 安全地访问缓冲区。 如果可以在 DISPATCH_LEVEL 调用操作后回调例程,则必须调用 FltDoCompletionProcessingWhenSafe 来写下操作,直到可以在 IRQL <= APC_LEVEL 处处理该操作。 FltDoCompletionProcessingWhenSafe 的 SafePostCallback 参数指向的回调例程应首先调用 FltLockUserBuffer 以锁定缓冲区,然后调用 MmGetSystemAddressForMdlSafe 以获取缓冲区的系统地址。
操作后回调例程应在快速 I/O 操作中处理缓冲区,如下所示:
使用缓冲区地址访问缓冲区 (,因为快速 I/O 操作不能具有 MDL) 。
为了确保用户空间缓冲区地址有效,微筛选器驱动程序必须使用一个例程(如 ProbeForRead 或 ProbeForWrite),将 try/ 中除块之外的所有缓冲区引用括起来。
保证在正确的线程上下文中调用快速 I/O 操作的后操作回调例程。
保证在 IRQL <= APC_LEVEL 调用快速 I/O 操作的后操作回调例程,因此它可以安全地调用 FltLockUserBuffer 等例程。
以下示例代码片段检查目录控制操作的系统缓冲区或快速 I/O 标志,并在必要时延迟完成处理。
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;
}
}
}
对于快速 I/O 或基于 IRP 的操作,除块外,所有缓冲区引用都应包含在 try/ 中。 尽管对于使用缓冲 I/O 的基于 IRP 的操作,无需包含这些引用,但 尝试/除块外 是一种安全预防措施。