デッドロックのデバッグ - DRIVER_VERIFIER_DETECTED_VIOLATION (C4): 0x1001

ドライバー検証ツール がスピン ロック階層違反を検出すると、ドライバーの検証ツールは、0x1001 のパラメーター 1 の値を持つ バグ チェック 0xC4 DRIVER_VERIFIER_DETECTED_VIOLATION を生成します

デッドロック検出オプションがアクティブな場合 (デッドロック検出はドライバー検証ツールの標準オプションの一部です) 、ドライバー検証ツール は、割り当てられた各スピン ロックと、取得および解放された順序を追跡します。 ロック階層違反とは、少なくとも 1 つのケースでは LockAが取得され、LockB が取得される前に LockA が保持され、 別のケースでは LockB が取得され、 LockA が必要になる前に 保持される状況をドライバー検証ツールが検出したことを意味します

重要: このバグチェックは、ドライバー検証ツール が階層違反が発生したことを検出するたびに 発生します。実際のデッドロック状態が発生している場合は発生しません。 この違反により、ドライバーのさまざまなコンポーネント間で共有されているロックは、常に 2 つのスレッドのデッドロックを不可能にする順序で取得および解放される必要があることを強く強制します。

Windows 8.1 の新機能 ドライバー検証ツール でこの違反が発生したとき 、デバッガーがアタッチされている場合、デバッガーはエラーに関する入力を求められます。 Windows 8 以前のバージョンの Windows では、この違反により、すぐにバグチェックが発生します。

************ 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 (Break) を選択 し、デバッガーの推奨コマンド (!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 (Display Stack Backtrace) コマンドを 使用して現在のスタック トレースを表示することを提案します。

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

デバッガーの出力は、問題のドライバーが 1 つのスレッドでロック B を取得する前にロック A を取得して保持することでこの規則に違反し、ロック B を取得し、別のスレッドでロック A を取得しようとしていることを示しています。 最初のスレッド(スレッド0)は終了しているので、これら2つのロックの取得とその後の解放は、ドライバー イメージがロードされてからのある時点で起こったことに注意してください。

テスト ドライバーの適切なシンボルが読み込まれると、デバッガーには、その時点でロックが取得された関数が表示されます。 この例では、ロック A とロック B の両方が同じ関数で取得されています。 スレッド 0 では、どちらも SystemControlIrpWorker 取得されます。 スレッド 1 では、どちらも DeviceControlIrpWorker 取得されます (!deadlock 3 からの ロック B 出力と現在のスタック出力 (kb) に示されています)。

この時点で、各関数のソース コードを確認すると、ロックをこのような順序で取得できるコード パスが存在することが明らかになります。

両方 の MyTestDriver!AlphaLockMyTestDriver!BravoLock は、ドライバーでグローバルに使用できるオブジェクトです。

include "MyTestDriverHeader.h"

// Locks used to control access to various objects
extern KSPIN_LOCK AlphaLock;
extern KSPIN_LOCK BravoLock;

関数 SystemControlIrpWorker の内部には、BravoLock (Lock B) の取得時に AlphaLock (!deadlock 出力の ロック 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 の再取得を待っている間に変更されないことが重要な場合は、より重要なコード変更が必要になる可能性があります。

スピンロックやその他の同期技術については、以下を参照してください。 スピンロック

スピン ロック

バグ チェック 0xC4: DRIVER_VERIFIER_DETECTED_VIOLATION

!deadlock