1. |
一定不要在沒有標註 I/O 要求封包 (IRP) 掛起 (IoMarkIrpPending) 的情況下通過調度常式返回 STATUS_PENDING。 |
2. |
一定不要通過插斷服務常式 (ISR) 調用 KeSynchronizeExecution。 它會使系統死結。 |
3. |
一定不要將 DeviceObject->Flags 設定為 DO_BUFFERED_IO 和 DO_DIRECT_IO。 它會擾亂系統並最終導致致命錯誤。 而且,一定不要在 DeviceObject->Flags 中設定 METHOD_BUFFERED、METHOD_NEITHER、METHOD_IN_DIRECT 或 METHOD_OUT_DIRECT,因為這些值只在定義 IOCTL 時使用。 |
4. |
一定不要通過頁面緩衝池分配發送器對象。 如果這樣做,將會偶爾導致系統故障檢測 (Bugcheck)。 |
5. |
當運行於 IRQL >= DISPATCH_LEVEL 時,一定不要通過頁面緩衝池分配記憶體,或訪問頁面緩衝池中的記憶體。 這是一個致命錯誤。 |
6. |
一定不要在 IRQL >= DISPATCH_LEVEL 上等候核心發送器對象出現非零間隔。 這是一個致命錯誤。 |
7. |
在 IRQL >= DISPATCH_LEVEL 上執行時,一定不要調用任何導致調用線程發生直接或間接等待的函數。 這是一個致命錯誤。 |
8. |
一定不要把插斷要求層級 (IRQL) 降低到低於您的頂級常式被調用的層級。 |
9. |
如果沒有調用過 KeRaiseIrql(),則一定不要調用 KeLowerIrql()。 |
10. |
一定不要使處理器 (KeStallExecutionProcessor) 停止運轉的時間超過 50 微秒。 |
11. |
一定不要使旋轉鎖 (Spin Lock) 保持鎖定狀態的時間超過您的需要。 要使系統獲得更好的總體效能,請不要使任何系統範圍內有效旋轉鎖的鎖定時間超過 25 微秒。 |
12. |
當 IRQL 大於 DISPATCH_LEVEL 時,一定不要調用 KeAcquireSpinLock 和 KeReleaseSpinLock,或 KeAcquireSpinLockAtDpcLevel 和 KeReleaseSpinLockFromDpcLevel。 |
13. |
一定不要通過調用 KeReleaseSpinLockFromDpcLevel 來釋放 KeAcquireSpinLock 所擷取的旋轉鎖,因為這會使原始 IRQL 無法被還原。 |
14. |
一定不要在 ISR 或 SynchCritSection 常式中調用 KeAcquireSpinLock 和 KeReleaseSpinLock 或者其它任何使用可執行旋轉鎖的常式。 |
15. |
當您在常式中而不是在 DriverEntry 中建立裝置對象時,一定不要忘記清除 DO_DEVICE_INITIALIZING 標記。 |
16. |
一定不要同時在不同處理器的多個線程中將延時程序呼叫 (DPC) 對象添加到隊列中(使用 KeInsertQueueDpc)。 這會導致致命錯誤。 |
17. |
一定不要通過 CutomerTimerDPC 常式釋放周期定時器。 您可以通過 DPC 常式釋放非周期定時器。 |
18. |
一定不要將相同的 DPC 指標傳遞給 KeSetTimer,或者 KeSetTimerEx (CustomTimerDpc) 和 KeInsertQueueDpc (CustomDpc),因為這將導致競爭。 |
19. |
旋轉鎖鎖定時,一定不要調用 IoStartNextPacket。 這將使系統死結。 |
20. |
旋轉鎖鎖定時,一定不要調用 IoCompleteRequest。 這將使系統死結。 |
21. |
如果您的驅動程式設定了完成常式,那麼一定不要在沒有把完成常式設定為 NULL 的情況下調用 IoCompleteRequest。 |
22. |
調用 IoCompleteRequest 之前,一定不要忘記設定 IRP 中的 I/O 狀態區。 |
23. |
在將 IRP 添加到隊列中或將它發送到另一個驅動程式 (IoCallDriver) 之後,一定不要調用 IoMarkPending。 在驅動程式調用 IoMarkPending 之前,IRP 可能已經完成,由此可能發生故障檢測。 對於包含完成常式的驅動程式,如果設定了 Irp->PendingReturned,則完成常式必須調用 IoMarkPending。 |
24. |
一定不要在已經對某個 IRP 調用 IoCompleteRequest 之後再去訪問該 IRP。 |
25. |
一定不要對不屬於您的驅動程式的 IRP 調用 IoCancelIrp,除非您知道該 IRP 還沒有完成。 |
26. |
在您的調度常式返回到調用者之前,一定不要對您的調度常式正在處理的 IRP 調用 IoCancelIrp。 |
27. |
一定不要從中間驅動程式調用 IoMakeAssociatedIrp 來為較低的驅動程式建立 IRP。 在中間驅動程式中所獲得的 IRP 可能是已被關聯的 IRP,而您不能將其它 IRP 關聯到已經被關聯的 IRP。 |
28. |
一定不要對使用緩衝 I/O 而設定的 IRP 調用 IoMakeAssociatedIrp。 |
29. |
一定不要簡單地將指向裝置 I/O 寄存器的虛擬指標解除引用並訪問這些指標。 始終使用正確的硬體抽象層 (HAL) 函數來訪問裝置。 |
30. |
如果 IRP 或裝置對象可能在 DISPATCH 層級被修改,那麼一定不要通過 ISR 來訪問 它。 在對稱式多處理器系統中,這會造成資料損毀。 |
31. |
正在進階 IRQL 中運行時,如果資料可能被低級 IROL 代碼寫入,那麼一定不要修改該資料。 應當使用 KeSynchronizeExecution 常式。 |
32. |
在擷取系統範圍的取消旋轉鎖 (IoAcquireCancelSpinLock) 之前,一定不要在您的 DispatchCleanup 常式中擷取驅動程式自己的旋轉鎖(如果有的話)。 要避免可能出現的死結,一定要在驅動程式中遵循一致的鎖定擷取階層。 |
33. |
一定不要在取消常式中調用 IoAcquireCancelSpinLock,因為該常式被調用時已經擷取了系統級的取消旋轉鎖。 |
34. |
在從取消常式返回之前,一定不要忘記調用 IoReleaseCancelSpinLock。 |
35. |
一定不要使用基於 IRQL 的同步,因為它只對單一處理器系統有效。 提高單一處理器上的 IRQL 將不會掩蔽在其它處理器上的中斷。 |
36. |
一定不要對重疊的記憶體位址範圍使用 RtlCopyMemory。 應當使用 RtlMoveMemory。 |
37. |
一定不要假定頁面大小是常量,即使是用於給定的 CPU。 為了保持可移植性,應當使用 PAGE_SIZE 以及在標頭檔中所定義的其它頁面相關常量。 |
38. |
一定不要從引導/系統初始化階段載入的驅動程式的 DriverEntry 常式中訪問除 Registry/Machine/Hardware 和 Registry/Machine/System 以外的任何登錄機碼。 |
39. |
一定不要為了載入驅動程式而在驅動程式的登錄機碼 (Registry/Machine/System/CurrentControlSet/Services) 下建立 Enum 項。 系統將動態地建立該項。 |
40. |
如果沒有先在註冊表中申請必需的與匯流排相關的 I/O 連接埠、記憶體範圍、中斷或直接記憶體存取 (DMA) 通道/連接埠等硬體資源,一定不要初始化物理裝置。 |
41. |
一定不要在您的 DriverEntry 常式調用 IoRegisterDriverReinitialization,除非重初始化常式返回了 STATUS_SUCCESS。 |
42. |
IRQL 為 PASSIVE_LEVEL 時,一定不要從被頁面調度的線程或驅動程式常式中在 Wait 參數被設定為 TRUE 的情況下調用 KeSetEvent。 如果碰巧在調用 KeSetEvent 和 KeWait..Object(s) 之間您的常式被頁面調度出去,這類調用就會導致致命的分頁錯誤。 |
43. |
與上例相同的條件下,同樣不能調用 KeReleaseSemaphore 。 |
44. |
與上例相同的條件下,同樣不能調用 KeReleaseMutex 。 |
45. |
一定不要通過零售的 Windows NT 驅動程式調用 KeBugCheckEx 或 KeBugCheck 來停止系統的運行,除非您遇到的是破壞系統記憶體並最終導致系統進入故障檢測的重要錯誤。 應當始終巧妙地處理錯誤條件。 |
46. |
一定不要假定 IoTimer 常式將會準確地在一秒邊界處被調用,因為任何特定 IoTimer 常式的調用間隔最終取決於系統時鐘。 |
47. |
一定不要從核心模式的裝置驅動程式調用 Win32 API (API)。 |
48. |
一定不要使用會導致堆疊溢位的遞迴函式,因為調用線程的核心模式堆棧不能動態增長。 |
49. |
在處理多個中斷的 ISR 常式中,一定不要使用中斷對象指標 (PKINTERRUPT) 來標識中斷,因為您在 ISR 中所獲得的中斷對象地址不會始終與您通過 IoConnectInterrupt 所獲得的地址相同。 要想識別當前發生中斷的裝置,應當僅使用您在 IoConnectInterrupt 中所指定的 ServiceContext 值。 |
50. |
如果沒有清零 CustomTimerDpc (KeCancelTimer),一定不要卸載驅動程式。 如果在卸載驅動程式後啟動 DPC,它可能調用不存在的代碼,並導致系統進入故障檢測查。 |
51. |
如果 IRP 中設定了某個驅動程式的 I/O CompletionRoutine,那麼一定要等到所有這些 IRP 完成之後,才能卸載該驅動程式。 如果卸載驅動程式後,IRP 被更低級的驅動程式完成,那麼系統會試圖執行不存在的代碼,並導致系統崩潰。 |
52. |
一定要等到驅動程式準備好要處理某個裝置中斷時,才能啟用該裝置中斷。 應當只在完成驅動程式初始化之後才啟用它,執行 ISR 和 DPC 時,系統才能安全的訪問裝置對象的若干私人成員。 |
53. |
在旋轉鎖鎖定時,一定不要調用驅動程式以外的代碼,因為這會引起死結。 |
54. |
如果您的驅動程式通過 IoBuildAsynchronousFsdRequest/IoAllocateIrp 建立了一個 IRP,那麼,一定不要從您的 I/O CompletionRoutine 為這個 IRP 返回 STATUS_MORE_PROCESSING_REQUIRED 以外的任何狀態,因為該 IRP 沒有為與完成有關的 I/O 管理器的處理後工作做好準備。 這樣的 IRP 應當被驅動程式顯式地釋放 (IoFreeIrp)。 如果本來沒有打算重用 IRP,可以在返回狀態 STATUS_MORE_PROCESSING_REQUIRED 之前,在 CompletionRoutine 中將它釋放。 |
55. |
一定不要在任意的線程上下文中使用 IoBuildSynchronousFsdRequest/IoBuildDeviceIoControlRequest 來分配 IRP,因為該 IRP 依然與該線程保持關聯 (Irp->ThreadListEntry),直到它被釋放。 |
56. |
如果已經使用 IoAllocateIrp 在 ChargeQuota 參數被設定為 TRUE 的情況下分配了某個 IRP,那麼一定不要對該 IRP 調用 IoInitializeIrp。 如果在 ChargeQuota 設定為 TRUE 的情況下分配 IRP,則 I/O 管理器將把它為該 IRP 分配記憶體時所用的緩衝池的相關資訊儲存在該 IRP 的內部標記中。 如果對這樣的 IRP 調用 IoInitializeIrp,那麼,當該函數盲目地清零整個 IRP 時,分配池資訊將會丟失。 當您釋放 IRP 時,這將導致記憶體被破壞。 同時,一定不要重用來自 IO 管理器的 IRP。 如果要重用 IRP,應當使用 IoAllocateIrp 分配您自己的 IRP。 |
57. |
如果在調用線程的堆棧中分配了對象,就一定不要在 KeWaitForSingleObject/KeWaitForMultipleObjects 中將 WaitMode 指定為 UserMode。 這樣做的結果是,如果被等候的對象是在函數堆棧中建立的,那麼您必須將 WaitMode 指定為 KernelMode 才能防止線程被頁面調度出去。 |
58. |
在沒有對關鍵節中的代碼加以保護的情況下,一定不要在使用者模式線程的上下文中擷取諸如 ERESOURCES 和 FastMutex(Unsafe) 這類資源。 因為擷取這些資源不會使 IRQL 提高到 APC_LEVEL,所以,如果線程在已擷取資源後被掛起(通過將 APC 排入佇列實現),它可能導致死結,並使系統安全性降低。 因此,應當通過顯式地將 IRQL 提高到 APC_LEVEL,或者調用 KeEnterCriticalRegion 來進入關鍵段,然後可以擷取這些資源。 |