Fehler in direkten E/A-Vorgängen

Das häufigste direkte E/A-Problem ist, dass Puffer mit nuller Länge nicht ordnungsgemäß verarbeitet werden. Da der E/A-Manager keine MDLs für Übertragungen der Länge Null erstellt, führt ein Puffer der Länge Null zu einem NULL-Wert bei Irp-MdlAddress>.

Um den Adressraum zuzuordnen, sollten Treiber MmGetSystemAddressForMdlSafe verwenden, was NULL zurückgibt, wenn die Zuordnung fehlschlägt, da ein Treiber einen NULL-MdlAddress übergibt. Treiber sollten immer auf eine NULL-Rückgabe überprüfen, bevor Sie versuchen, die zurückgegebene Adresse zu verwenden.

Direkte E/A umfasst eine doppelte Zuordnung des Adressraums des Benutzers zu einem Systemadresspuffer, sodass zwei unterschiedliche virtuelle Adressen dieselbe physische Adresse haben. Die Doppelzuordnung hat die folgenden Folgen, was manchmal zu Problemen bei Treibern führen kann:

  • Der Offset auf der virtuellen Seite der Adresse des Benutzers wird zum Offset auf der Systemseite.

    Der Zugriff über das Ende dieser Systempuffer hinaus kann je nach Seiten granularität der Zuordnung für lange Zeiträume unbemerkt bleiben. Sofern der Puffer eines Aufrufers nicht am Ende einer Seite zugeordnet ist, werden daten, die über das Ende des Puffers geschrieben wurden, dennoch im Puffer angezeigt, und der Aufrufer weiß nicht, dass ein Fehler aufgetreten ist. Wenn das Ende des Puffers mit dem Ende einer Seite übereinstimmt, könnte die virtuelle Systemadresse über das Ende auf etwas verweisen oder ungültig sein. Solche Probleme können äußerst schwierig zu finden sein.

  • Wenn der aufrufende Prozess einen anderen Thread aufweist, der die Zuordnung des Speichers des Benutzers ändert, ändert sich der Inhalt des Systempuffers, wenn sich die Speicherzuordnung des Benutzers ändert.

    In diesem Fall kann die Verwendung des Systempuffers zum Speichern von Scratchdaten Zu Problemen führen. Zwei Abrufe vom gleichen Speicherspeicherort können unterschiedliche Werte liefern.

    Der folgende Codeausschnitt empfängt eine Zeichenfolge in einer direkten E/A-Anforderung und versucht dann, diese Zeichenfolge in Großbuchstaben zu konvertieren:

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

    Da der Puffer möglicherweise nicht ordnungsgemäß gebildet ist, versucht der Code, einen Unicode-NULL-Wert als letztes Pufferzeichen zu erzwingen. Wenn der zugrunde liegende physische Speicher jedoch sowohl einem Benutzer- als auch einer Kernelmodusadresse doubly zugeordnet ist, kann ein anderer Thread im Prozess den Puffer überschreiben, sobald dieser Schreibvorgang abgeschlossen ist.

    Wenn der NULL-Wert nicht vorhanden ist, kann der Aufruf von RtlInitUnicodeString den Bereich des Puffers überschreiten und möglicherweise zu einer Fehlerüberprüfung führen, wenn er außerhalb der Systemzuordnung liegt.

Wenn ein Treiber eine eigene MDL erstellt und zuordnet, sollte sichergestellt werden, dass er nur mit der Methode zugreift, für die er eine Prüfung ausgeführt hat. Das heißt, wenn der Treiber MmProbeAndLockPages aufruft, gibt er eine Zugriffsmethode an (IoReadAccess, IoWriteAccess oder IoModifyAccess). Wenn der Treiber IoReadAccess angibt, darf er nicht später versuchen, in den Systempuffer zu schreiben, der von MmGetSystemAddressForMdl oder MmGetSystemAddressForMdlSafe zur Verfügung gestellt wird.