访问后操作回调例程中的用户缓冲区

微筛选器驱动程序 操作后回调例程 应在基于 IRP 的 I/O 操作中处理缓冲区,如下所示:

  • 检查缓冲区是否存在 MDL。 MDL 指针可以在操作FLT_PARAMETERSMdlAddressOutputMdlAddress 参数中找到。 微筛选器驱动程序可以调用 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 处处理该操作。 FltDoCompletionProcessingWhenSafeSafePostCallback 参数指向的回调例程应首先调用 FltLockUserBuffer 以锁定缓冲区,然后调用 MmGetSystemAddressForMdlSafe 以获取缓冲区的系统地址。

操作后回调例程应在快速 I/O 操作中处理缓冲区,如下所示:

  • 使用缓冲区地址访问缓冲区 (,因为快速 I/O 操作不能具有 MDL) 。

  • 为了确保用户空间缓冲区地址有效,微筛选器驱动程序必须使用一个例程(如 ProbeForReadProbeForWrite),将 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 的操作,无需包含这些引用,但 尝试/除块外 是一种安全预防措施。