最常見的直接 I/O 問題是無法正確處理零長度緩衝區。 因為 I/O 管理員不會為零長度傳輸建立 MDL,所以零長度緩衝區會在 Irp-MdlAddress> 產生 Null 值。
若要對應位址空間,驅動程式應該使用 MmGetSystemAddressForMdlSafe,如果對應失敗,則會傳回 Null,因為如果驅動程式傳遞 NullMdlAddress,則會傳回 Null。 驅動程式應該一律檢查 Null 傳回,再嘗試使用傳回的位址。
直接 I/O 牽涉到將使用者的位址空間雙重對應至系統位址緩衝區,讓兩個不同的虛擬位址具有相同的實體位址。 雙重映射具有以下後果,有時會給驅動程式帶來問題:
使用者位址虛擬頁面的位移會成為系統頁面的位移。
超出這些系統緩衝區結尾的存取可能會在很長一段時間內不被注意,具體取決於映射的頁面粒度。 除非呼叫端的緩衝區配置在頁面結尾附近,否則寫入緩衝區結尾之外的數據仍會出現在緩衝區中,而且呼叫端將不知道發生任何錯誤。 如果緩衝區的結尾與頁面結尾重合,則結尾以外的系統虛擬位址可能會指向任何專案,或可能無效。 此類問題可能極難發現。
如果呼叫進程有另一個執行緒來修改使用者的記憶體對應,則當使用者的記憶體對應變更時,系統緩衝區的內容將會變更。
在此情況下,使用系統緩衝區來儲存暫存資料可能會導致問題。 從相同記憶體位置取得的兩個擷取可能會產生不同的值。
下列程式碼片段會在直接 I/O 要求中接收字串,然後嘗試將該字串轉換成大寫字元:
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);因為緩衝區可能未正確形成,所以程式代碼會嘗試強制 Unicode Null 作為最後一個緩衝區字元。 不過,如果基礎實體記憶體被雙重映射到使用者模式和核心模式位址,那麼當寫入操作完成時,程序中的其他執行緒可以立即覆蓋緩衝區。
相反地,如果 NULL 不存在,則呼叫 RtlInitUnicodeString 可能會超出緩衝區的範圍,而且如果它落在系統映射之外,可能會導致系統錯誤檢查。
如果驅動程式建立並對應自己的 MDL,它應該確保它只使用已探查的方法來存取 MDL。 也就是說,當驅動程式呼叫 MmProbeAndLockPages 時,它會指定存取方法 (IoReadAccess、 IoWriteAccess 或 IoModifyAccess) 。 如果驅動程式指定 IoReadAccess,則稍後不得嘗試寫入 MmGetSystemAddressForMdl 或 MmGetSystemAddressForMdlSafe 所提供的系統緩衝區。