次の方法で共有


操作後コールバック ルーチン内でユーザー バッファーへアクセスする

ミニフィルター ドライバーの操作後コールバック ルーチンは、IRP ベースの I/O 操作でバッファーを次のように処理する必要があります。

  • バッファーに MDL が存在するかどうかを確認します。 MDL ポインターは、操作用の FLT_PARAMETERSMdlAddress パラメーターまたは OutputMdlAddress パラメーターにあります。 ミニフィルター ドライバーでは、FltDecodeParameters を呼び出して MDL ポインターのクエリを実行できます。

    有効な MDL を取得する 1 つの方法は、コールバック データ内の I/O パラメーター ブロック FLT_IO_PARAMETER_BLOCKMinorFunction メンバー内で 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/except ブロック内のすべてのバッファー参照を囲む必要があります。

  • 高速 I/O 操作の操作後コールバック ルーチンは、正しいスレッド コンテキストで呼び出されていることが保証されます。

  • 高速 I/O 操作の操作後コールバック ルーチンは、IRQL <= APC_LEVEL で呼び出されることが保証されているため、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/except ブロックで囲む必要があります。 バッファー I/O を使用する IRP ベースの操作ではこれらの参照を囲む必要がありませんが、try/except ブロックは安全対策です。