共用方式為


緩衝區處理

或許任何驅動程式內最常見的錯誤都與緩衝區處理有關,其中緩衝區無效或太小。 這些錯誤可能會允許緩衝區溢位或造成系統當機,這構成系統的安全性危害。

從驅動程式的觀點來看,緩衝區有兩種類型之一:

  • 分頁緩衝區,可能或可能不在記憶體中。

  • 非分頁緩衝區,其必須位於記憶體中。

當然,不正確位址不是分頁或非分頁位址,但是當作業系統開始解決分頁錯誤這類緩衝區原因時,它會將不正確位址隔離成其中一個「標準」位址範圍, (分頁核心位址、非分頁核心位址或使用者位址) 並引發適當的錯誤類型。 緩衝區錯誤一律會由錯誤檢查 (PAGE_FAULT_IN_NONPAGED_AREA來處理,例如) 或例外狀況 (STATUS_ACCESS_VIOLATION,例如) 。 在錯誤檢查的情況下,系統會停止作業。 如果是例外狀況,則會叫用堆疊型例外狀況處理常式,如果其中沒有任何處理程式處理例外狀況,則會叫用錯誤檢查。

不論為何,任何可能導致驅動程式導致驅動程式導致錯誤檢查的應用程式程式所呼叫的存取路徑,都是驅動程式內的安全性違規。 這可讓應用程式對整個系統造成拒絕服務攻擊。

此區域中最常見的問題之一,就是驅動程式寫入器會假設操作環境太多。 這可能包括:

  • 檢查位址中是否已設定高位。 這不適用於系統使用四 GB 微調 (4GT) 的 x86 型電腦,方法是在 Boot.ini 檔案中設定 /3GB 選項。 在此情況下,使用者模式位址會為位址空間的第三 GB (GB) 設定高位。

  • 使用 ProbeForReadProbeForWrite 來驗證位址。 雖然這可確保位址在探查時是有效的使用者模式位址,但探查作業之後不需要它保持有效。 因此,這項技術引進了一種細微的競爭條件,可能會導致定期無法產生損毀。 ProbeForReadProbeForWrite 呼叫是必要的原因:若要驗證位址是否為使用者模式位址,而且緩衝區的長度在使用者位址範圍內。 如果省略探查,使用者可以傳入有效的核心模式位址,而__try並__except區塊 (結構化例外狀況處理) ,並開啟大型安全性漏洞。 因此 ,ProbeForReadProbeForWrite 呼叫是必要的,以確保對齊,而且使用者模式位址加上長度是在使用者位址範圍內。 不過,需要__try和__except區塊,才能防範存取。

    請注意 ,ProbeForRead 只會驗證位址和長度是否落在可能的使用者模式位址範圍內, (4GT 的系統稍微低於 2 GB,例如) ,而不是記憶體位址是否有效。 相反地, ProbeForWrite 會嘗試存取所指定長度的每個頁面中的第一個位元組,以確認這些是有效的記憶體位址。

  • 依賴記憶體管理員函式 (MmIsAddressValid,例如) 確保位址有效。 如同探查函式,這引進了競爭條件,可能會導致無法產生損毀。

  • 無法使用結構化例外狀況處理。 編譯器內的__try和__except函式會使用作業系統層級支援來處理例外狀況。 呼叫 ExRaiseStatus或其中一個相關函式,會擲回核心層級的例外狀況。 驅動程式無法針對任何可能會引發例外狀況的呼叫使用結構化例外狀況處理,將會導致錯誤檢查 (通常KMODE_EXCEPTION_NOT_HANDLED) 。

    請注意,針對不預期引發錯誤的程式碼使用結構化例外狀況處理是錯誤的錯誤。 這只會遮罩其他找到的實際 Bug。 將__try和__except包裝函式放在常式的最上層不是此問題的正確解決方案,但有時是由驅動程式寫入器嘗試的反射解決方案。

  • 依賴剩餘穩定使用者記憶體的內容。 例如,假設驅動程式將值寫入使用者模式記憶體位置,然後在相同的常式中稍後參考該記憶體位置。 惡意應用程式可能會主動修改該記憶體,因此會導致驅動程式當機。

對於檔案系統而言,這些問題特別嚴重,因為它們通常依賴直接存取使用者緩衝區, (METHOD_NEITHER傳輸方法) 。 這類驅動程式會直接操作使用者緩衝區,因此必須納入緩衝處理的預防措施方法,以避免作業系統層級損毀。 快速 I/O 一律會傳遞原始記憶體指標,因此如果支援快速 I/O,驅動程式必須防範類似的問題。

WDK 包含 FASTFAT 和 CDFS 檔案系統範例程式碼中的許多緩衝區驗證範例,包括:

  • fastfat\deviosup.c 中的FatLockUserBuffer函式會使用MmProbeAndLockPages來鎖定使用者緩衝區背後的實體頁面,並在FatMapUserBuffer中鎖定MmGetSystemAddressForMdlSafe,為鎖定的頁面建立虛擬對應。

  • fastfat\fsctl.c 中的FatGetVolumeBitmap函式會使用ProbeForRead 和 ProbeForWrite來驗證重組 API 中的使用者緩衝區。

  • cdfs\read.c 中的 CdCommonRead 函式會使用__try,並將程式碼周圍__except為零使用者緩衝區。 請注意, CdCommonRead 中的範例程式碼似乎使用 try 和 except 關鍵字。 在 WDK 環境中,C 中的這些關鍵字會以編譯器延伸模組__try和__except來定義。 任何使用 C++ 程式碼的人都必須使用原生編譯器類型來正確處理例外狀況,因為__try是 C++ 關鍵字,但不是 C 關鍵字,而且會提供對核心驅動程式不正確 C++ 例外狀況處理形式。