Erreurs dans les E/S directes

Le problème d’E/S direct le plus courant est l’échec de la gestion correcte des mémoires tampons de longueur nulle. Étant donné que le gestionnaire d’E/S ne crée pas de DLL pour les transferts de longueur nulle, une mémoire tampon de longueur nulle génère une valeur NULL sur Irp-MdlAddress>.

Pour mapper l’espace d’adressage, les pilotes doivent utiliser MmGetSystemAddressForMdlSafe, qui retourne NULL si le mappage échoue, comme si un pilote transmet une MdlAddressNULL. Les pilotes doivent toujours case activée pour un retour NULL avant d’essayer d’utiliser l’adresse retournée.

Les E/S directes impliquent le double mappage de l’espace d’adressage de l’utilisateur à une mémoire tampon d’adresses système, afin que deux adresses virtuelles différentes aient la même adresse physique. La double cartographie a les conséquences suivantes, ce qui peut parfois entraîner des problèmes pour les pilotes :

  • Le décalage dans la page virtuelle de l’adresse de l’utilisateur devient le décalage dans la page système.

    L’accès au-delà de la fin de ces mémoires tampons système peut passer inaperçu pendant de longues périodes en fonction de la granularité des pages du mappage. Sauf si la mémoire tampon d’un appelant est allouée à la fin d’une page, les données écrites au-delà de la fin de la mémoire tampon apparaissent néanmoins dans la mémoire tampon, et l’appelant ignore qu’une erreur s’est produite. Si la fin de la mémoire tampon coïncide avec la fin d’une page, les adresses virtuelles système au-delà de la fin peuvent pointer vers quelque chose ou être non valides. De tels problèmes peuvent être extrêmement difficiles à trouver.

  • Si le processus appelant a un autre thread qui modifie le mappage de la mémoire de l’utilisateur, le contenu de la mémoire tampon système change lorsque le mappage de mémoire de l’utilisateur change.

    Dans ce cas, l’utilisation de la mémoire tampon système pour stocker des données de travail peut entraîner des problèmes. Deux extractions à partir du même emplacement de mémoire peuvent produire des valeurs différentes.

    L’extrait de code suivant reçoit une chaîne dans une demande d’E/S directe, puis tente de convertir cette chaîne en caractères majuscules :

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

    Étant donné que la mémoire tampon n’est peut-être pas correctement formée, le code tente de forcer une valeur Unicode NULL comme dernier caractère de mémoire tampon. Toutefois, si la mémoire physique sous-jacente est doublement mappée à une adresse utilisateur et à une adresse en mode noyau, un autre thread du processus peut remplacer la mémoire tampon dès que cette opération d’écriture est terminée.

    À l’inverse, si la valeur NULL n’est pas présente, l’appel à RtlInitUnicodeString peut dépasser la plage de la mémoire tampon et éventuellement provoquer un bogue case activée s’il se trouve en dehors du mappage système.

Si un pilote crée et mappe son propre MDL, il doit s’assurer qu’il accède à la MDL uniquement avec la méthode pour laquelle il a sondé. Autrement dit, lorsque le pilote appelle MmProbeAndLockPages, il spécifie une méthode d’accès (IoReadAccess, IoWriteAccess ou IoModifyAccess). Si le pilote spécifie IoReadAccess, il ne doit pas tenter ultérieurement d’écrire dans la mémoire tampon système rendue disponible par MmGetSystemAddressForMdl ou MmGetSystemAddressForMdlSafe.