사후 콜백 루틴에서 사용자 버퍼 액세스
미니필터 드라이버 사후 콜백 루틴 은 다음과 같이 IRP 기반 I/O 작업에서 버퍼를 처리해야 합니다.
버퍼에 대한 MDL이 있는지 확인합니다. MDL 포인터는 작업에 대한 FLT_PARAMETERSMdlAddress 또는 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 플래그는 읽기 및 쓰기 작업에 대해서만 설정할 수 있습니다. 루틴이 모든 작업에 대해 유효한 MDL을 확인하므로 FltDecodeParameters 를 사용하여 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 사후 콜백 루틴을 호출할 수 있는 경우 IRQL <= APC_LEVEL 처리될 때까지 FltDoCompletionProcessingWhenSafe를 호출하여 작업을 보류해야 합니다. FltDoCompletionProcessingWhenSafe의 SafePostCallback 매개 변수가 가리키는 콜백 루틴은 먼저 FltLockUserBuffer를 호출하여 버퍼를 잠근 다음 MmGetSystemAddressForMdlSafe를 호출하여 버퍼에 대한 시스템 주소를 가져와야 합니다.
사후 콜백 루틴은 다음과 같이 빠른 I/O 작업에서 버퍼를 처리해야 합니다.
버퍼 주소를 사용하여 버퍼에 액세스합니다(빠른 I/O 작업에 MDL이 있을 수 없기 때문).
사용자 공간 버퍼 주소가 유효한지 확인하려면 미니필터 드라이버는 ProbeForRead 또는ProbeForWrite와 같은 루틴을 사용하여 블록을제외한try/의 모든 버퍼 참조를 묶어야 합니다.
빠른 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/로 묶어야 합니다. 버퍼링된 I/O를 사용하는 IRP 기반 작업에 대해 이러한 참조를 묶을 필요는 없지만 블록을제외한시도는/ 안전한 예방 조치입니다.