Compartilhar via


Erros na E/S Direta

O problema de E/S direto mais comum é não lidar com buffers de comprimento zero corretamente. Como o gerenciador de E/S não cria MDLs para transferências de comprimento zero, um buffer de comprimento zero resulta em um valor NULL em Irp-MdlAddress>.

Para mapear o espaço de endereço, os drivers devem usar MmGetSystemAddressForMdlSafe, que retorna NULL se o mapeamento falhar, como fará se um driver passar por um NULLMdlAddress. Os drivers sempre devem verificar se há um retorno NULL antes de tentar usar o endereço retornado.

A E/S direta envolve o mapeamento duplo do espaço de endereço do usuário para um buffer de endereço do sistema, de modo que dois endereços virtuais diferentes tenham o mesmo endereço físico. O mapeamento duplo tem as seguintes consequências, que às vezes podem causar problemas para os drivers:

  • O deslocamento para a página virtual do endereço do usuário converte-se em deslocamento para a página do sistema.

    O acesso além do final desses buffers do sistema pode passar despercebido por longos períodos de tempo, dependendo da granularidade da página do mapeamento. A menos que o buffer de um chamador seja alocado perto do final de uma página, os dados gravados além do final do buffer, no entanto, aparecerão no buffer e o chamador não saberá que ocorreu qualquer erro. Se o final do buffer coincidir com o final de uma página, os endereços virtuais do sistema além do final poderão apontar para qualquer coisa ou podem ser inválidos. Esses problemas podem ser extremamente difíceis de encontrar.

  • Se o processo de chamada tiver outro thread que modifique o mapeamento da memória do usuário, o conteúdo do buffer do sistema será alterado quando o mapeamento de memória do usuário for alterado.

    Nessa situação, usar o buffer do sistema para armazenar dados de arranhões pode causar problemas. Duas buscas do mesmo local de memória podem produzir valores diferentes.

    O snippet de código a seguir recebe uma cadeia de caracteres em uma solicitação de E/S direta e tenta converter essa cadeia de caracteres em caracteres maiúsculos:

    PWCHAR  PortName = NULL;
    
    PortName = (PWCHAR)MmGetSystemAddressForMdlSafe(irp->MdlAddress, NormalPagePriority);
    
    //
    // Null-terminate the PortName so that RtlInitUnicodeString will not
    // be invalid.
    //
    PortName[Size / sizeof(WCHAR) - 1] = UNICODE_NULL;
    
    RtlInitUnicodeString(&AdapterName, PortName);
    

    Como o buffer pode não estar formado corretamente, o código tenta forçar um NULL Unicode como o último caractere de buffer. No entanto, se a memória física subjacente for mapeada duas vezes, tanto para um endereço em modo de usuário quanto para um endereço em modo kernel, outro thread no processo pode sobrescrever o buffer assim que esta operação de gravação for concluída.

    Por outro lado, se o NULL não estiver presente, a chamada para RtlInitUnicodeString poderá exceder o intervalo do buffer e possivelmente causar uma verificação de bugs se ele estiver fora do mapeamento do sistema.

Se um driver cria e mapeia seu próprio MDL, ele deve garantir que o acesse somente com o método para o qual ele foi sondado. Ou seja, quando o driver chama MmProbeAndLockPages, ele especifica um método de acesso (IoReadAccess, IoWriteAccess ou IoModifyAccess). Se o driver especificar IoReadAccess, ele não deverá tentar gravar posteriormente no buffer do sistema disponibilizado por MmGetSystemAddressForMdl ou MmGetSystemAddressForMdlSafe.