IRQL_UNEXPECTED_VALUE NdisFIndicateReceiveNetBufferLists 后蓝屏?

IRQL_UNEXPECTED_VALUE BSOD after NdisFIndicateReceiveNetBufferLists?

我们有一个 NDIS LWF 驱动程序,只有在极少数系统上,我们在 NdisFIndicateReceiveNetBufferLists 上得到 IRQL_UNEXPECTED_VALUE BSOD,但是我们没有在代码的任何部分提高或降低 IRQL,并且 NdisFIndicateReceiveNetBufferLists 是在 irp_mj_device_control 回调中调用。我们还检查 IRQL,如果它是 DISPATCH,我们将最后一个参数设置为 NDIS_RECEIVE_FLAGS_DISPATCH_LEVEL,否则为 0,这可能是问题所在吗?

我也找到了这篇文章:

https://knowledge.broadcom.com/external/article/164146/crash-with-bug-check-0xc8-after-installi.html

他们有一个类似的问题,问题似乎是另一个 NDIS 驱动程序将 IRQL 提高到 DISPATCH_LEVEL 而忘记降低它?但我仍然不确定这是否适用于我们的问题?这也是我们的问题吗?

IRQL_UNEXPECTED_VALUE (c8)
The processor's IRQL is not what it should be at this time.  This is
usually caused by a lower level routine changing IRQL for some period
and not restoring IRQL at the end of that period (eg acquires spinlock
but doesn't release it).
Arguments:
Arg1: 0000000000020002, (Current IRQL << 16) | (Expected IRQL << 8) | UniqueValue
Arg2: fffff82621a444f0, Depends on UniqueValue:
    If UniqueValue is 0 or 1: APC->KernelRoutine.
    If UniqueValue is 2: the callout routine
    If UniqueValue is 3: the interrupt's ServiceRoutine
    If UniqueValue is 0xfe: 1 iff APCs are disabled
Arg3: ffff950cf4dccff0, Depends on UniqueValue:
    If UniqueValue is 0 or 1: APC
    If UniqueValue is 2: the callout's parameter
    If UniqueValue is 3: KINTERRUPT
Arg4: 0000000000000000, Depends on UniqueValue:
    If UniqueValue is 0 or 1: APC->NormalRoutine

调用堆栈:

nt!KeBugCheckEx
nt!KeExpandKernelStackAndCalloutInternal
nt!KeExpandKernelStackAndCalloutEx
ndis!ndisInvokeNextReceiveHandler
ndis!ndisFilterIndicateReceiveNetBufferLists
ndis!NdisFIndicateReceiveNetBufferLists
OurNdis

第二个参数是调用例程(基于唯一值),是 ndis!ndisDataPathExpandStackCallback。

编辑 1:

我做了更多的挖掘,实际上 ndisDataPathExpandStackCallback 似乎只是调用了 ndisCallReceiveHandler(它没有出现在堆栈上)。我假设这只是向其他 NDIS 驱动程序指示已接收的 NBL?无论如何,ndisDataPathExpandStackCallback 是通过 KeExpandKernelStackAndCalloutInternal 调用的,后者存储 IRQL,并在调用后检查 IRQL,如果不匹配,则引发此错误检查,bingo!

但是,现在我的问题是,如何找到有问题的驱动程序?我能否以某种方式使用 ndiskd 扩展来帮助我调用 KeExpandKernelStackAndCalloutInternal 的 NDIS 驱动程序,以便我可以证明并找到有问题的驱动程序?

虽然通过调查堆栈,我确实找到了 pacer!PcFilterReceiveNetBufferLists,但我怀疑这是有问题的驱动程序,因为它是 windows 驱动程序,对吗?

They had a similar issue, and the issue seems to be that there was another NDIS driver raising the IRQL to DISPATCH_LEVEL and forgeting to lower it? But I'm still not sure if this is applicable to our issue or not? Could this be also our issue?

那个特定的错误检查意味着有人在已经从堆栈中展开的代码中泄露了 IRQL。 KeExpandKernelStackAndCalloutInternal 正在做这样的事情:

oldIrql = KeGetCurrentIrql();
(*callback)(...);
newIrql = KeGetCurrentIrql();

if (oldIrql != newIrql) {
    KeBugCheckEx(IRQL_UNEXPECTED_VALUE, (newIrql << 16) | (oldIrql << 8) | 2, ...);
}

解码第一个参数,这意味着 IRQL 在进入时为 PASSIVE_LEVEL,在退出时为 DISPATCH_LEVEL。

不幸的是,执行此操作的代码已经完成 运行ning -- 这个错误检查只是确定他们在离开房间之前没有清理这个地方。通过查看 !ndiskd.miniport 中的过滤器 driver 堆栈,您可以对可能 运行ning 的代码做出有根据的猜测。但这只是给了你一个起点:根据从网络传入的数据包,网络堆栈可能调用了各种 drivers。例如,如果网络指示了一个 SMB3 数据包,那么执行实际上会一直绕过文件系统堆栈。因此,列出所有可能具有 运行.

的可能 driver 并不是特别容易

不过,需要检查的一件事是您是否正确使用了 NDIS_RECEIVE_FLAGS_DISPATCH_LEVEL 标志。仅当您确定 IRQL 当前为 DISPATCH_LEVEL 时才允许设置标志。如果该标志使用不当,您可能会欺骗其他 driver 使 IRQL 不匹配。例如,假设的 driver 可能有:

void FilterReceiveNbls(..., ULONG ReceiveFlags) {
    KIRQL oldIrql;
    KeRaiseCurrentIrql(DISPATCH_LEVEL, &oldIrql);

    . . . do stuff at dispatch level . . .

    if (0 == (ReceiveFlags & NDIS_RECEIVE_FLAGS_DISPATCH_LEVEL)) {
        KeLowerCurrentIrql(oldIrql);
    }
}

我不能肯定地说这就是发生的事情。我只是在寻找您可以在 driver 中审核的内容,正确使用 NDIS_RECEIVE_FLAGS_DISPATCH_LEVEL 就是其中之一。请注意,始终 不将此标志添加到 ReceiveFlags 是正确的。 (事实上​​ ,如果您看到其他人设置了标志,则清除该标志甚至是正确的 - 该标志的唯一好处是非常小的性能优化。)因此,如果您有疑问,请不要添加该标志。

Windows 11 如果启用 Driver Verifier (DV) 并启用 NDIS/WIFI 选项,则可以严格验证此标志。最简单的方法是在所有 driver 上启用 DV,但如果 运行 太慢,您可以 select 每个单独的网络 driver。在 Windows 11 上,当使用 NDIS/WIFI 选项启用 DV 时,如果任何 driver 滥用任何 NDIS_XXX_DISPATCH_LEVEL 标志,您将在错误。

(DV 目前 验证 driver returns IRQL 到其原始水平——这对未来来说是个好主意,不过.)