完成例程跑在 DISPATCH_LEVEL 时,用户缓冲区不能再碰
写过滤驱动时,最容易在完成例程里直接碰用户缓冲区。PASSIVE_LEVEL 能碰分页内存,派发到 DISPATCH_LEVEL 之后就不行了,蓝屏码经常是 IRQL_NOT_LESS_OR_EQUAL。几个我现在固定检查的点:
1. 派发例程开头用 KeGetCurrentIrql 记一笔。如果例程可能被完成例程在 DISPATCH_LEVEL 回调,用户缓冲区必须提前在 PASSIVE_LEVEL 用 MmGetSystemAddressForMdlSafe 映射好,完成例程里只碰这个系统地址。
2. 不要在 DISPATCH_LEVEL 调 ZwXxx、ExAllocatePoolWithTag(PagedPool) 或任何会等待的对象。需要推迟就用 IoQueueWorkItem,回到 PASSIVE_LEVEL 再做。
3. 共享的设备扩展计数用 InterlockedIncrement,不要用普通 ++。自旋锁持有期间不能访问分页内存,锁外再碰用户缓冲。
4. 取消例程和完成例程会并发。IRP 的状态位用 InterlockedExchange 交接,取消例程里 IoReleaseCancelSpinLock 之后不要再假设自己还拥有这个 IRP。
这几条守住之后,注册表缓存、文件过滤这类“完成例程里再写一笔”的代码就不容易在高 IRQL 上踩分页内存。 膜拜神贴,后面的请保持队形~
页:
[1]