首次开发内核驱动调用FwpsCalloutRegister触发IRQL_NOT_LESS_OR_EQUAL崩溃求助
排查DriverEntry中FwpsCalloutRegister导致的IRQL_NOT_LESS_OR_EQUAL崩溃
刚上手第一个内核驱动就碰到这种崩溃,太闹心了!我来帮你拆解一下这个问题,一步步找出原因。
首先,WinDbg给出的IRQL_NOT_LESS_OR_EQUAL (a)错误本质很明确:当前CPU运行的IRQL级别过高,却尝试访问了只能在更低IRQL下访问的内存(比如可分页内存),或者直接访问了无效地址。
针对你在DriverEntry里调用status = FwpsCalloutRegister(wdfDevice, &sCallout, &Globals.FlowCalloutId);崩溃的情况,重点排查这几个方向:
1. 检查sCallout结构体的初始化问题
sCallout里的回调函数(比如分类回调、通知回调)是崩溃的高发区:
- 确保所有回调函数都被编译在非分页代码段里。如果你的回调函数用了
__declspec(code_seg("PAGE"))标记,那它会被放在可分页内存中,一旦FwpsCalloutRegister内部或后续回调在高于PASSIVE_LEVEL的IRQL下调用它,必然崩溃。去掉这个标记即可(内核驱动默认代码段是非分页的,除非你手动修改了)。 - 检查结构体里的字符串成员(比如
displayName):如果是UNICODE_STRING,要确保它指向的缓冲区是非分页内存,不能用用户态内存或者堆分配的分页内存(比如用ExAllocatePoolWithTag时要指定NonPagedPoolNx类型)。
2. 确认Globals.FlowCalloutId的内存属性
FlowCalloutId是输出参数,FwpsCalloutRegister会往这个地址写数据:
- 如果
Globals是你定义的全局变量,默认是在非分页数据段里,但如果你给它加了__declspec(data_seg("PAGE"))标记,那它会被放到分页内存中。当调用FwpsCalloutRegister时,如果IRQL被提升(比如内部用到了DPC),写入分页内存就会触发崩溃。 - 绝对不能把
FlowCalloutId放在DriverEntry的栈上!内核栈在某些场景下会被分页,把栈变量地址传给需要在高IRQL下访问的函数,大概率会触发崩溃。必须把FlowCalloutId放在全局非分页内存,或者用ExAllocatePoolWithTag分配的非分页内存里。
3. 检查DriverEntry中的IRQL状态
虽然DriverEntry默认是在PASSIVE_LEVEL运行,但如果你在调用FwpsCalloutRegister之前手动提升了IRQL(比如调用了KeRaiseIrqlToDpcLevel),又没调用KeLowerIrql降回去,那此时访问任何分页内存都会触发崩溃。崩溃后可以用WinDbg的!irql命令确认当时的IRQL级别。
额外WinDbg调试技巧
- 用
!address <崩溃地址>命令查看崩溃时访问的地址属性,确认是不是分页内存。 - 用
!pool <地址>检查地址是否属于内核池,以及池的类型(分页/非分页)。 - 执行
k命令查看调用栈,确认FwpsCalloutRegister内部是在哪个环节触发的崩溃,定位到具体代码行。
举个正确的示例参考:
// 全局非分页变量存储CalloutId FWPS_CALLOUT_ID g_FlowCalloutId; // 回调函数确保是非分页(默认就是,无需额外标记) VOID NTAPI MyClassifyFn( IN const FWPS_INCOMING_VALUES0* inFixedValues, IN const FWPS_INCOMING_METADATA_VALUES0* inMetaValues, IN OUT VOID* layerData, IN const FWPS_FILTER0* filter, IN UINT64 flowContext, OUT FWPS_CLASSIFY_OUT0* classifyOut ) { // 回调逻辑实现 } NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) { NTSTATUS status; WDFDEVICE wdfDevice; FWPS_CALLOUT sCallout = {0}; // 省略WDF设备初始化逻辑... // 初始化sCallout结构体 sCallout.calloutKey = L"MySampleCallout"; sCallout.classifyFn = MyClassifyFn; // 其他必要成员初始化... // 调用注册函数,传入全局非分页变量地址 status = FwpsCalloutRegister(wdfDevice, &sCallout, &g_FlowCalloutId); if (!NT_SUCCESS(status)) { // 错误处理逻辑 } return status; }
内容的提问来源于stack exchange,提问作者wezten
相关产品推荐
相关产品推荐

