Erros na E/S direta

O problema de E/S direta mais comum é não lidar corretamente com buffers de comprimento zero. 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 retornará NULL se o mapeamento falhar, como acontecerá se um driver passar um MdlAddress NULL. 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 motoristas:

  • O deslocamento para a página virtual do endereço do usuário torna-se o 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 aparecerão no buffer e o chamador não saberá que ocorreu algum 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 poderão 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 temporários 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 Unicode NULL como o último caractere de buffer. No entanto, se a memória física subjacente for mapeada duplamente para um endereço de usuário e um endereço de modo kernel, outro thread no processo poderá substituir o buffer assim que essa 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 bug se estiver fora do mapeamento do sistema.

Se um driver criar e mapear seu próprio MDL, ele deverá garantir que ele acesse o MDL somente com o método para o qual ele investigou. 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.