Zugreifen auf Benutzerpuffer in einer Rückrufroutine nach der Operation

Eine Rückrufroutine für Minifiltertreiber nach der Operation sollte einen Puffer in einem IRP-basierten E/A-Vorgang wie folgt behandeln:

  • Überprüfen Sie, ob für den Puffer eine MDL vorhanden ist. Der MDL-Zeiger befindet sich im Parameter MdlAddress oder OutputMdlAddress im FLT_PARAMETERS für den Vorgang. Minifiltertreiber können FltDecodeParameters aufrufen, um den MDL-Zeiger abzufragen.

    Eine Methode zum Abrufen einer gültigen MDL besteht darin, im MinorFunction-Member des E/A-Parameterblocks, FLT_IO_PARAMETER_BLOCK, in den Rückrufdaten nach dem flag IRP_MN_MDL zu suchen. Im folgenden Beispiel wird gezeigt, wie Sie nach dem flag IRP_MN_MDL suchen.

    NTSTATUS status;
    PMDL *ReadMdl = NULL;
    PVOID ReadAddress = NULL;
    
    if (FlagOn(CallbackData->Iopb->MinorFunction, IRP_MN_MDL))
    {
        ReadMdl = &CallbackData->Iopb->Parameters.Read.MdlAddress;
    }
    

    Das IRP_MN_MDL-Flag kann jedoch nur für Lese- und Schreibvorgänge festgelegt werden. Es ist am besten, FltDecodeParameters zum Abrufen einer MDL zu verwenden, da die Routine auf eine gültige MDL für jeden Vorgang überprüft. Im folgenden Beispiel wird nur der MDL-Parameter zurückgegeben, wenn er gültig ist.

    NTSTATUS status;
    PMDL *ReadMdl = NULL;
    PVOID ReadAddress = NULL;
    
    status = FltDecodeParameters(CallbackData, &ReadMdl, NULL, NULL, NULL);
    
  • Wenn für den Puffer eine MDL vorhanden ist, rufen Sie MmGetSystemAddressForMdlSafe auf, um die Systemadresse für den Puffer zu erhalten, und verwenden Sie diese Adresse dann für den Zugriff auf den Puffer. (MmGetSystemAddressForMdlSafe kann unter IRQL <= DISPATCH_LEVEL aufgerufen werden.)

    Ausgehend vom vorherigen Beispiel ruft der folgende Code die Systemadresse ab.

    if (*ReadMdl != NULL)
    {
        ReadAddress = MmGetSystemAddressForMdlSafe(*ReadMdl, NormalPagePriority);
        if (ReadAddress == NULL)
        {
            CallbackData->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
            CallbackData->IoStatus.Information = 0;
        }
    }
    
  • Wenn keine MDL für den Puffer vorhanden ist, überprüfen Sie mithilfe des Makros FLT_IS_SYSTEM_BUFFER , ob das Systempufferflag für den Vorgang festgelegt ist.

    • Wenn das FLT_IS_SYSTEM_BUFFER Makro TRUE zurückgibt, verwendet der Vorgang gepufferte E/A, und auf den Puffer kann sicher unter IRQL = DISPATCH_LEVEL zugegriffen werden.

    • Wenn das FLT_IS_SYSTEM_BUFFER Makro FALSE zurückgibt, kann unter IRQL = DISPATCH_LEVEL nicht sicher auf den Puffer zugegriffen werden. Wenn die Postoperation-Rückrufroutine unter DISPATCH_LEVEL aufgerufen werden kann, muss fltDoCompletionProcessingWhenSafe aufgerufen werden, um den Vorgang zu verwenden, bis er unter IRQL <= APC_LEVEL verarbeitet werden kann. Die Rückrufroutine, auf die der SafePostCallback-Parameter von FltDoCompletionProcessingWhenSafe verweist, sollte zuerst FltLockUserBuffer aufrufen, um den Puffer zu sperren, und dann MmGetSystemAddressForMdlSafe aufrufen, um die Systemadresse für den Puffer abzurufen.

Eine Postoperation-Rückrufroutine sollte einen Puffer in einem schnellen E/A-Vorgang wie folgt behandeln:

  • Verwenden Sie die Pufferadresse, um auf den Puffer zuzugreifen (da ein schneller E/A-Vorgang keine MDL haben kann).

  • Um sicherzustellen, dass eine Pufferadresse für den Benutzerbereich gültig ist, muss der Minifiltertreiber eine Routine wie ProbeForRead oder ProbeForWrite verwenden und alle Pufferverweiseaußer Blöcken in try/ einschließen.

  • Die Postoperation-Rückrufroutine für einen schnellen E/A-Vorgang wird garantiert im richtigen Threadkontext aufgerufen.

  • Die Postoperation-Rückrufroutine für einen schnellen E/A-Vorgang wird garantiert unter IRQL <= APC_LEVEL aufgerufen, sodass Routinen wie FltLockUserBuffer sicher aufgerufen werden können.

Das folgende Beispielcodefragment überprüft den Systempuffer oder schnelle E/A-Flags für einen Verzeichnissteuerungsvorgang und verzögert bei Bedarf die Verarbeitung des Abschlusses.

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;
        }
    }
}

Bei Vorgängen, die entweder schnell E/A oder IRP-basiert sein können, sollten alle Pufferverweise mitAusnahme von Blöcken in try/ eingeschlossen werden. Obwohl Sie diese Verweise nicht für IRP-basierte Vorgänge einschließen müssen, die gepufferte E/A verwenden, stellen die Try-Blöcke/ eine sichere Vorsichtsmaßnahme dar.