Errori nell'I/O diretto
Il problema di I/O diretto più comune non riesce a gestire correttamente i buffer di lunghezza zero. Poiché il gestore di I/O non crea mdls per i trasferimenti di lunghezza zero, un buffer di lunghezza zero restituisce un valore NULL in Irp-MdlAddress>.
Per eseguire il mapping dello spazio degli indirizzi, i driver devono usare MmGetSystemAddressForMdlSafe, che restituisce NULL se il mapping ha esito negativo, perché se un driver passa un MdlAddress NULL. I driver devono sempre verificare la presenza di un valore NULL restituito prima di tentare di usare l'indirizzo restituito.
L'I/O diretto comporta il doppio mapping dello spazio indirizzi dell'utente a un buffer di indirizzi di sistema, in modo che due indirizzi virtuali diversi abbiano lo stesso indirizzo fisico. Il doppio mapping ha le conseguenze seguenti, che talvolta possono causare problemi per i driver:
L'offset nella pagina virtuale dell'indirizzo dell'utente diventa l'offset nella pagina di sistema.
L'accesso oltre la fine di questi buffer di sistema può andare inosservato per lunghi periodi di tempo a seconda della granularità della pagina del mapping. A meno che il buffer di un chiamante non venga allocato alla fine di una pagina, i dati scritti oltre la fine del buffer verranno comunque visualizzati nel buffer e il chiamante non sarà a conoscenza di eventuali errori. Se la fine del buffer coincide con la fine di una pagina, gli indirizzi virtuali di sistema oltre la fine potrebbero puntare a qualsiasi elemento o potrebbero non essere validi. Tali problemi possono essere estremamente difficili da trovare.
Se il processo chiamante ha un altro thread che modifica il mapping dell'utente della memoria, il contenuto del buffer di sistema cambierà quando cambia il mapping della memoria dell'utente.
In questo caso, l'uso del buffer di sistema per archiviare i dati scratch può causare problemi. Due recupero dalla stessa posizione di memoria potrebbero produrre valori diversi.
Il frammento di codice seguente riceve una stringa in una richiesta di I/O diretta, quindi tenta di convertire tale stringa in caratteri maiuscoli:
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);
Poiché il buffer potrebbe non essere formato correttamente, il codice tenta di forzare un valore NULL Unicode come ultimo carattere di buffer. Tuttavia, se la memoria fisica sottostante viene mappata in modo doubly a un indirizzo utente e in modalità kernel, un altro thread nel processo può sovrascrivere il buffer non appena viene completata l'operazione di scrittura.
Viceversa, se il valore NULL non è presente, la chiamata a RtlInitUnicodeString può superare l'intervallo del buffer ed eventualmente causare un controllo del bug se non rientra nel mapping di sistema.
Se un driver crea ed esegue il mapping del proprio MDL, deve assicurarsi che acceda al file MDL solo con il metodo per cui è stato eseguito il probe. Ovvero, quando il driver chiama MmProbeAndLockPages, specifica un metodo di accesso (IoReadAccess, IoWriteAccess o IoModifyAccess). Se il driver specifica IoReadAccess, non deve tentare successivamente di scrivere nel buffer di sistema reso disponibile da MmGetSystemAddressForMdl o MmGetSystemAddressForMdlSafe.