Compartilhar via


Acessando buffers de usuário em uma rotina de retorno de chamada do Postoperation

Uma rotina de retorno de chamada do postoperation do driver de minifiltro deve tratar um buffer em uma operação de E/S baseada em IRP da seguinte maneira:

  • Verifique se existe um MDL para o buffer. O ponteiro MDL pode ser encontrado no parâmetro MdlAddress ou OutputMdlAddress no FLT_PARAMETERS da operação. Os drivers de minifiltro podem chamar FltDecodeParameters para consultar o ponteiro MDL.

    Um método para obter um MDL válido é procurar o sinalizador IRP_MN_MDL no membro MinorFunction do bloco de parâmetros de E/ S, FLT_IO_PARAMETER_BLOCK, nos dados de retorno de chamada. O exemplo a seguir mostra como marcar para o sinalizador 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;
    }
    

    No entanto, o sinalizador IRP_MN_MDL pode ser definido apenas para operações de leitura e gravação. É melhor usar FltDecodeParameters para recuperar um MDL, pois a rotina verifica se há um MDL válido para qualquer operação. No exemplo a seguir, somente o parâmetro MDL será retornado se for válido.

    NTSTATUS status;
    PMDL *ReadMdl = NULL;
    PVOID ReadAddress = NULL;
    
    status = FltDecodeParameters(CallbackData, &ReadMdl, NULL, NULL, NULL);
    
  • Se existir um MDL para o buffer, chame MmGetSystemAddressForMdlSafe para obter o endereço do sistema para o buffer e, em seguida, use esse endereço para acessar o buffer. (MmGetSystemAddressForMdlSafe pode ser chamado em IRQL <= DISPATCH_LEVEL.)

    Continuando com o exemplo anterior, o código a seguir obtém o endereço do sistema.

    if (*ReadMdl != NULL)
    {
        ReadAddress = MmGetSystemAddressForMdlSafe(*ReadMdl, NormalPagePriority);
        if (ReadAddress == NULL)
        {
            CallbackData->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
            CallbackData->IoStatus.Information = 0;
        }
    }
    
  • Se não houver MDL para o buffer, marcar se o sinalizador de buffer do sistema está definido para a operação usando a macro FLT_IS_SYSTEM_BUFFER.

    • Se a macro FLT_IS_SYSTEM_BUFFER retornar TRUE, a operação usará E/S em buffer e o buffer poderá ser acessado com segurança em IRQL = DISPATCH_LEVEL.

    • Se a macro FLT_IS_SYSTEM_BUFFER retornar FALSE, o buffer não poderá ser acessado com segurança em IRQL = DISPATCH_LEVEL. Se a rotina de retorno de chamada postoperation puder ser chamada em DISPATCH_LEVEL, ela deverá chamar FltDoCompletionProcessingWhenSafe para aguardar a operação até que ela possa ser processada em IRQL <= APC_LEVEL. A rotina de retorno de chamada apontada pelo parâmetro SafePostCallback de FltDoCompletionProcessingWhenSafe deve primeiro chamar FltLockUserBuffer para bloquear o buffer e, em seguida, chamar MmGetSystemAddressForMdlSafe para obter o endereço do sistema para o buffer.

Uma rotina de retorno de chamada de postoperation deve tratar um buffer em uma operação de E/S rápida da seguinte maneira:

  • Use o endereço do buffer para acessar o buffer (porque uma operação de E/S rápida não pode ter um MDL).

  • Para garantir que um endereço de buffer de espaço do usuário seja válido, o driver de minifiltro deve usar uma rotina como ProbeForRead ou ProbeForWrite, incluindo todas as referências de buffer em try/, exceto blocos.

  • A rotina de retorno de chamada de postoperation para uma operação de E/S rápida tem a garantia de ser chamada no contexto de thread correto.

  • A rotina de retorno de chamada de postoperation para uma operação de E/S rápida tem a garantia de ser chamada em IRQL <= APC_LEVEL, para que possa chamar com segurança rotinas como FltLockUserBuffer.

O fragmento de código de exemplo a seguir verifica o buffer do sistema ou sinalizadores de E/S rápidos para uma operação de controle de diretório e adia o processamento de conclusão, se necessário.

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

Para operações que podem ser rápidas de E/S ou baseadas em IRP, todas as referências de buffer devem ser colocadas em try/, exceto blocos. Embora você não precise incluir essas referências para operações baseadas em IRP que usam E/S em buffer, os blocos try/except são uma precaução segura.