调试死锁 - DRIVER_VERIFIER_DETECTED_VIOLATION (C4):0x1001
当 驱动程序验证程序 检测到旋转锁层次结构冲突时,驱动程序验证程序 将生成 bug 检查0xC4:参数 1 值为 0x1001 的DRIVER_VERIFIER_DETECTED_VIOLATION。
当死锁检测选项处于活动状态 (死锁检测是驱动程序验证程序标准选项) 的一部分时, 驱动程序验证程序 会跟踪分配的每个旋转锁以及获取和释放它的顺序。 锁层次结构冲突意味着驱动程序验证程序检测到至少在一种情况下, 在获取 LockB 之前获取并持有 LockA ,在另一种情况下, 在需要 LockA 之前获取并保留 LockB 。
重要每当驱动程序验证程序检测到发生层次结构冲突时,而不是在发生实际死锁情况时,就会出现此 bug 检查。 此冲突强制强制要求始终按顺序获取和释放驱动程序的各个组件之间共享的任何锁,这会使两个线程无法死锁。
Windows 8.1当驱动程序验证程序遇到此冲突时,如果附加了调试器,调试器将要求你输入有关错误的输入。 在 Windows 8 和早期版本的 Windows 中,此冲突会导致立即检查 bug。
************ Verifier Detected a Potential Deadlock *************
**
** Type !deadlock in the debugger for more information.
**
*****************************************************************
*** Verifier assertion failed ***
(B)reak, (I)gnore, (W)arn only, (R)emove assert?
若要在运行 Windows 8.1 的计算机上调试此冲突,请选择“B (中断) ”,然后输入建议的调试器命令 (!deadlock) :
kd> !deadlock
issue: 00001001 97dd800c 86858ce0 00000000
Deadlock detected (2 locks in 2 threads):
Thread 0: A B.
Thread 1: B A.
Where:
Thread 0 = TERMINATED.
Thread 1 = c4ae2040.
Lock A = 97dd800c (MyTestDriver!AlphaLock+0x00000000) - Type 'Spinlock'.
Lock B = 97dd8008 (MyTestDriver!BravoLock+0x00000000) - Type 'Spinlock'.
!deadlock3 命令还可用于显示更多信息,包括上次获取时的堆栈:
kd> !deadlock 3
issue: 00001001 97dd800c 86858ce0 00000000
Deadlock detected (2 locks in 2 threads):
#
Thread 0: TERMINATED took locks in the following order:
Lock A = 97dd800c (MyTestDriver!AlphaLock+0x00000000) - Type 'Spinlock'.
Node: 8685acd8
Stack: 97dd65b7 MyTestDriver!SystemControlIrpWorker+0x00000027
97dd605a MyTestDriver!Dispatch_SystemControl+0x0000001a
820c4b4d nt!IovCallDriver+0x000002cc
81ca3772 nt!IofCallDriver+0x00000062
81eb165e nt!IopSynchronousServiceTail+0x0000016e
81eb5518 nt!IopXxxControlFile+0x000003e8
81eb4516 nt!NtDeviceIoControlFile+0x0000002a
81d27e17 nt!KiSystemServicePostCall+0x00000000
Lock B = 97dd8008 (MyTestDriver!BravoLock+0x00000000) - Type 'Spinlock'.
Node: 86833578
Stack: 97dd65c5 MyTestDriver!SystemControlIrpWorker+0x00000a4a
97dd605a MyTestDriver!Dispatch_SystemControl+0x0000001a
820c4b4d nt!IovCallDriver+0x000002cc
81ca3772 nt!IofCallDriver+0x00000062
81eb165e nt!IopSynchronousServiceTail+0x0000016e
81eb5518 nt!IopXxxControlFile+0x000003e8
81eb4516 nt!NtDeviceIoControlFile+0x0000002a
81d27e17 nt!KiSystemServicePostCall+0x00000000
#
Thread 1: c4ae2040 (ThreadEntry = 86833a08) took locks in the following order:
Lock B = 97dd8008 (MyTestDriver!BravoLock+0x00000000) - Type 'Spinlock'.
Node: 86858ce0
Stack: 97dd65ef MyTestDriver!DeviceControlIrpWorker+0x0000005f
97dd605a MyTestDriver!Dispatch_DeviceControl+0x0000001a
820c4b4d nt!IovCallDriver+0x000002cc
81ca3772 nt!IofCallDriver+0x00000062
81eb165e nt!IopSynchronousServiceTail+0x0000016e
81eb5518 nt!IopXxxControlFile+0x000003e8
81eb4516 nt!NtDeviceIoControlFile+0x0000002a
81d27e17 nt!KiSystemServicePostCall+0x00000000
Lock A = 97dd800c (MyTestDriver!AlphaLock+0x00000000) - Type 'Spinlock'.
Stack: << Current stack trace - use kb to display it >>
调试器建议使用 kb (显示堆栈回溯) 命令显示当前堆栈跟踪。
kd> kb
ChildEBP RetAddr Args to Child
89b2cac4 820da328 97dd800c 86858ce0 00000000 nt!VfReportIssueWithOptions+0x86
89b2caf4 820d92a2 00000001 00000000 97dd65fd nt!ViDeadlockAnalyze+0x1d1
89b2cb7c 820d424e 86858ce0 00000000 97dd65fd nt!VfDeadlockAcquireResource+0x2fd
89b2cb9c 97dd65fd 00007780 89b2cbbc 97dd605a nt!VerifierKfAcquireSpinLock+0x8c
89b2cba8 97dd605a 9a9e7780 88d08f48 00000000 MyTestDriver!DeviceControlIrpWorker+0x54a
89b2cbbc 820c4b4d 9a9e7780 88d08f48 820c4881 MyTestDriver!Dispatch_DeviceControl+0x1a
(Inline) -------- -------- -------- -------- nt!IopfCallDriver+0x47
89b2cbe0 81ca3772 81eb165e b3c9ff80 88d08f48 nt!IovCallDriver+0x2cc
89b2cbf4 81eb165e 88d08fdc 88d08f48 00000000 nt!IofCallDriver+0x62
调试器输出显示,在一个线程上获取锁 B 之前,有问题的驱动程序通过获取并持有锁 A 来违反此规则,现在已获取锁 B,并正在尝试在另一个线程上获取锁 A。 请注意,线程 0) (的第一个线程已终止,因此这两个锁的获取和后续释放发生在驱动程序映像加载后的某个时间点。
加载测试驱动程序的正确符号后,调试器将显示当时获取锁的函数。 在此示例中,锁 A 和锁 B 都是在同一函数中获取的。 在 Thread 0 中,它们都是在 SystemControlIrpWorker 中获取的。 在线程 1 中,它们都获取在 DeviceControlIrpWorker (显示在 !deadlock 3 的锁 B 输出和当前堆栈输出 (kb) 中。
此时,查看每个函数的源代码应会发现存在一个代码路径,其中可以按此类顺序获取锁。
两个 MyTestDriver!AlphaLock 和 MyTestDriver!BravoLock 是驱动程序中全局可用的对象:
include "MyTestDriverHeader.h"
// Locks used to control access to various objects
extern KSPIN_LOCK AlphaLock;
extern KSPIN_LOCK BravoLock;
在函数 SystemControlIrpWorker 中,存在一个路径,在获取 BravoLock (锁 B) 时,获取并持有 !死锁 输出) 中的 AlphaLock (锁 A。 另请注意,锁按获取锁的相反顺序正确释放。 (以下代码经过大量编辑,仅显示生成此方案所需的元素) 。
NTSTATUS SystemControlIrpWorker(_In_ PIRP Irp)
{
KIRQL IrqlAlpha;
KIRQL IrqlBravo;
// <<Other local variable declarations removed>>
// <<Various lines of code not shown>>
KeAcquireSpinLock (&AlphaLock, &IrqlAlpha);
// <<Various lines of code not shown>>
KeAcquireSpinLock (&BravoLock, &IrqlBravo);
// <<Various lines of code not shown>>
KeReleaseSpinLock (&BravoLock, IrqlBravo);
KeReleaseSpinLock (&AlphaLock, IrqlAlpha);
// <<Various lines of code not shown>>
}
如果查看以下 DeviceControlIrpWorker 示例函数,可以看到可以按相反的顺序获取锁。 也就是说,在尝试获取 AlphaLock 时,可以获取并持有 BravoLock。 下面的示例已简化,但它表明可能存在发生冲突的路径。
NTSTATUS DeviceControlIrpWorker(_In_ PIRP Irp,
_In_ BOOLEAN bSomeCondition)
{
KIRQL IrqlAlpha;
KIRQL IrqlBravo;
// <<Other local variable declarations removed>>
if (bSomeCondition == FALSE)
{
//
// Note that if bSomeCondition is FALSE, then AlphaLock is acquired here
// If bSomeCondition is TRUE, it is not needed to be acquired right now
//
KeAcquireSpinLock (&AlphaLock, &IrqlAlpha);
}
// <<Various lines of code not shown>>
KeAcquireSpinLock (&BravoLock, &IrqlBravo);
// <<Various lines of code not shown>>
if (bSomeCondition == TRUE)
{
//
// Need to acquire AlphaLock here for upcoming code logic
//
KeAcquireSpinLock (&AlphaLock, &IrqlAlpha);
// <<Various lines of code not shown>>
KeReleaseSpinLock (&AlphaLock, IrqlAlpha);
}
// <<Various lines of code not shown>>
KeReleaseSpinLock (&BravoLock, IrqlBravo);
if (bSomeCondition == FALSE)
{
//
// Release the AlphaLock, which was acquired much earlier
//
KeReleaseSpinLock (&AlphaLock, IrqlAlpha);
}
// <<Various lines of code not shown>>
}
若要解决此潜在冲突,正确的做法是确保每当驱动程序尝试获取 AlphaLock 时,它都会检查并确保 BravoLock 未保留。 最简单的解决方法可能是只需释放 BravoLock,并在获取 AlphaLock 后立即重新获取它。 但是,如果 BravoLock 保护的任何数据在等待 AlphaLock 和重新获取 BravoLock 时不会更改,则可能需要进行更重大的代码更改。
有关旋转锁和其他同步技术的详细信息,请参阅 旋转锁。
相关主题
反馈
https://aka.ms/ContentUserFeedback。
即将发布:在整个 2024 年,我们将逐步淘汰作为内容反馈机制的“GitHub 问题”,并将其取代为新的反馈系统。 有关详细信息,请参阅:提交和查看相关反馈