Compartir vía


Errores en 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, ya que si un controlador pasa un MdlAddress NULL. 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, que a veces pueden 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 la página de la asignación. A menos que el búfer de un autor de la llamada se asigne 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 cualquier cosa o podría no ser válida. 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 cero 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 se haya formado correctamente, el código intenta forzar un valor NULL Unicode como último carácter de búfer. Sin embargo, si la memoria física subyacente se asigna doblemente a una dirección de usuario y en modo kernel, otro subproceso del proceso puede sobrescribir el búfer en cuanto 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 en el búfer del sistema disponible por MmGetSystemAddressForMdl o MmGetSystemAddressForMdlSafe.