Errores en la E/S directa

El problema de E/S directa más común es no controlar correctamente los búferes de longitud cero. Dado que el administrador de E/S no crea MDL para transferencias de longitud cero, un búfer de longitud cero da como resultado un valor NULL en Irp-MdlAddress>.

Para asignar el espacio de direcciones, los controladores deben usar MmGetSystemAddressForMdlSafe, que devuelve NULL si se produce un error en la asignación, como si un controlador pasa un MdlAddressNULL. Los controladores siempre deben comprobar si hay una devolución NULL antes de intentar usar la dirección devuelta.

La E/S directa implica la asignación doble del espacio de direcciones del usuario a un búfer de direcciones del sistema, de modo que dos direcciones virtuales diferentes tengan la misma dirección física. La asignación doble tiene las siguientes consecuencias, lo que a veces puede causar problemas para los conductores:

  • El desplazamiento en la página virtual de la dirección del usuario se convierte en el desplazamiento en la página del sistema.

    El acceso más allá del final de estos búferes del sistema puede pasar desapercibido durante largos períodos de tiempo en función de la granularidad de página de la asignación. A menos que se asigne el búfer de un llamador cerca del final de una página, los datos escritos más allá del final del búfer aparecerán en el búfer y el autor de la llamada no será consciente de que se ha producido ningún error. Si el final del búfer coincide con el final de una página, las direcciones virtuales del sistema más allá del final podrían apuntar a algo o podrían no ser válidos. Estos problemas pueden ser extremadamente difíciles de encontrar.

  • Si el proceso de llamada tiene otro subproceso que modifica la asignación del usuario de la memoria, el contenido del búfer del sistema cambiará cuando cambie la asignación de memoria del usuario.

    En esta situación, el uso del búfer del sistema para almacenar datos de arañazos puede causar problemas. Dos capturas de la misma ubicación de memoria pueden producir valores diferentes.

    El siguiente fragmento de código recibe una cadena en una solicitud de E/S directa y, a continuación, intenta convertir esa cadena en caracteres en mayúsculas:

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

    Dado que es posible que el búfer no tenga el formato correcto, el código intenta forzar un valor NULL Unicode como el último carácter de búfer. Sin embargo, si la memoria física subyacente se asigna doblemente a una dirección de usuario y modo kernel, otro subproceso del proceso puede sobrescribir el búfer tan pronto como se complete esta operación de escritura.

    Por el contrario, si el valor NULL no está presente, la llamada a RtlInitUnicodeString puede superar el intervalo del búfer y, posiblemente, provocar una comprobación de errores si se encuentra fuera de la asignación del sistema.

Si un controlador crea y asigna su propia MDL, debe asegurarse de que accede a MDL solo con el método para el que ha sondado. Es decir, cuando el controlador llama a MmProbeAndLockPages, especifica un método de acceso (IoReadAccess, IoWriteAccess o IoModifyAccess). Si el controlador especifica IoReadAccess, no debe intentar escribir más adelante en el búfer del sistema disponible por MmGetSystemAddressForMdl o MmGetSystemAddressForMdlSafe.