교착 상태 디버깅 - DRIVER_VERIFIER_DETECTED_VIOLATION(C4): 0x1001

드라이버 검증 도구가 스핀 잠금 계층 구조 위반을 감지하면 Driver Verifiergenerates Bug Check 0xC4: 매개 변수 1 값이 0x1001 DRIVER_VERIFIER_DETECTED_VIOLATION.

교착 상태 검색 옵션이 활성 상태인 경우(교착 상태 검색은 드라이버 검증 도구 표준 옵션의 일부임) 드라이버 검증 도구 는 할당된 각 스핀 잠금과 획득 및 해제된 순서를 추적합니다. 잠금 계층 위반은 드라이버 검증 도구가 LockB를 획득하기 전에 LockA를 획득하고 보유하는 상황을 드라이버 검증 도구가 감지했음을 의미하며, 다른 경우에는 LockA가 필요하기 전에 LockB를 획득하고 보유하는 상황을 감지했습니다.

중요 이 버그 검사 드라이버 검증 도구가 실제 교착 상태가 발생할 때가 아니라 계층 위반이 발생했음을 감지할 때마다 발생합니다. 이 위반은 드라이버의 다양한 구성 요소 간에 공유되는 모든 잠금을 항상 획득하고 해제하여 두 스레드의 교착 상태를 불가능하게 만드는 순서대로 해제하도록 강력하게 적용합니다.

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(중단)를 선택하고 제안된 디버거 명령(!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(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

디버거 출력은 문제의 드라이버가 한 스레드에서 Lock B를 획득하기 전에 Lock A를 획득하고 보유하여 이 규칙을 위반했음을 보여 하며, 이제 Lock B를 획득하고 다른 스레드에서 Lock A를 획득하려고 합니다. 첫 번째 스레드(스레드 0)가 종료되었으므로 드라이버 이미지가 로드된 이후 특정 시점에 이러한 두 잠금의 획득 및 후속 릴리스가 발생했습니다.

테스트 드라이버에 대한 적절한 기호가 로드되면 디버거는 당시 잠금을 획득한 함수를 표시합니다. 이 예제에서는 Lock A와 Lock B가 모두 동일한 함수에서 획득됩니다. 스레드 0에서는 둘 다 SystemControlIrpWorker에서 획득됩니다. 스레드 1에서 둘 다 DeviceControlIrpWorker 에서 획득됩니다( !deadlock 3 의 잠금 B 출력과 현재 스택 출력(kb)에 표시됨).

이 시점에서 각 함수의 소스 코드를 검토하면 이러한 순서로 잠금을 획득할 수 있는 코드 경로가 있음을 알 수 있습니다.

MyTestDriver! AlphaLockMyTestDriver! 브라보록은 드라이버에서 전역적으로 사용할 수 있는 개체입니다.

include "MyTestDriverHeader.h"

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

SystemControlIrpWorker 함수 내에는 AlphaLock(!deadlock 출력의 잠금 A)이 획득되고 브라보록(Lock B)이 획득될 때 유지되는 경로가 있습니다. 잠금이 획득되는 역순으로 올바르게 해제된다는 것도 주목할 가치가 있습니다. (다음 코드는 이 시나리오를 생성하는 데 필요한 요소만 표시하도록 많이 편집됩니다.)

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을 획득하려고 할 때 브라보록을 획득하고 보유할 수 있습니다. 다음 예제는 간소화되었지만 위반이 발생할 수 있는 경로가 있음을 보여줍니다.

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을 획득하려고 할 때마다 브라보록이 유지되지 않도록 하는 것입니다. 가장 간단한 수정은 AlphaLock을 획득하는 즉시 브라보록을 해제하고 다시 획득하는 것입니다. 그러나 AlphaLock 및 브라보록의 재 취득을 기다리는 동안 브라보록이 보호하는 데이터가 변경되지 않는 것이 중요한 경우 더 중요한 코드 변경이 필요할 수 있습니다.

스핀 잠금 및 기타 동기화 기술에 대한 자세한 내용은 스핀 잠금을 참조하세요.

스핀 잠금

버그 검사 0xC4: DRIVER_VERIFIER_DETECTED_VIOLATION

!교착 상태