操作後コールバック ルーチン内でユーザー バッファーへアクセスする
ミニフィルター ドライバーの操作後コールバック ルーチンは、IRP ベースの I/O 操作でバッファーを次のように処理する必要があります。
バッファーに MDL が存在するかどうかを確認します。 MDL ポインターは、操作用の FLT_PARAMETERS の MdlAddress パラメーターまたは OutputMdlAddress パラメーターにあります。 ミニフィルター ドライバーでは、FltDecodeParameters を呼び出して MDL ポインターのクエリを実行できます。
有効な MDL を取得する 1 つの方法は、コールバック データ内の I/O パラメーター ブロック FLT_IO_PARAMETER_BLOCK の 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/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 ブロックは安全対策です。