다음을 통해 공유


교착 상태 디버깅 - 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'.

!deadlock 3 명령을 사용하여 마지막 획득 시점의 스택을 비롯한 추가 정보를 표시할 수도 있습니다.

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

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

테스트 드라이버에 대한 적절한 기호가 로드되면 디버거는 당시 잠금이 획득된 함수를 표시합니다. 이 예제에서는 잠금 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 함수 내부에는 브라보록(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을 획득하려고 할 때 브라보록을 획득하고 보유할 수 있습니다. 다음 예제는 간소화되었지만 위반이 발생할 수 있는 경로가 있음을 보여줍니다.

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

!교착 상태