Erreurs dans les E/S directes
Le problème d’E/S direct le plus courant ne parvient pas à gérer correctement les 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 entraîne une valeur NULL sur Irp-MdlAddress>.
Pour mapper l’espace d’adressage, les pilotes doivent utiliser MmGetSystemAddressForMdlSafe, qui retourne NULL en cas d’échec du mappage, car si un pilote transmet une mdlAddress NULL. Les pilotes doivent toujours rechercher un retour NULL avant de tenter d’utiliser l’adresse retournée.
Les E/S directes impliquent un double mappage de l’espace d’adressage de l’utilisateur à une mémoire tampon d’adressage système, afin que deux adresses virtuelles différentes aient la même adresse physique. Le double mappage a les conséquences suivantes, ce qui peut parfois entraîner des problèmes pour les conducteurs :
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é de la page du mappage. Sauf si la mémoire tampon d’un appelant est allouée près de 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 ne sait pas 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 n’importe quoi ou ne peuvent pas être non valides. Ces 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 à zéro 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 requête 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 peut ne pas être correctement formée, le code tente de forcer une valeur Null Unicode comme dernier caractère de mémoire tampon. Toutefois, si la mémoire physique sous-jacente est doublement mappée à un 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 se termine.
À 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 une vérification de bogue si elle 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 au 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.