Compartilhar via


Depuração de deadlocks: DRIVER_VERIFIER_DETECTED_VIOLATION (C4): 0x1001

Quando o Verificador de Driver detecta uma violação da hierarquia de bloqueio de rotação, ele gera a Verificação de bug 0xC4: DRIVER_VERIFIER_DETECTED_VIOLATION com um valor de parâmetro 1 de 0x1001.

Quando a opção Detecção de Deadlock está ativa (a Detecção de Deadlock faz parte das Opções Padrão do Verificador de Driver), o Verificador de Driver controla cada bloqueio de rotação alocado e a ordem em que ele foi adquirido e liberado. Uma violação de hierarquia de bloqueio significa que o Verificador de Driver detectou uma situação em que, em pelo menos um caso, Bloqueio A é adquirido e mantido antes de Bloqueio B ser adquirido e, em outro, Bloqueio B é adquirido e mantido antes de Bloqueio A ser necessário.

Importante Essa verificação de bug ocorre sempre que o Verificador de Driver detecta que a violação de hierarquia ocorreu, não quando uma situação real de deadlock está ocorrendo. Essa violação impõe fortemente que todos os bloqueios compartilhados entre os vários componentes de um driver devem sempre ser adquiridos e liberados em uma ordem que torna o deadlock de dois threads impossível.

Novidade no Windows 8.1 Quando o Verificador de Driver encontrar essa violação, se o depurador estiver anexado, o depurador solicitará a entrada sobre o erro. No Windows 8 e em versões anteriores do Windows, essa violação resulta em uma verificação de bug imediata.

************ 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?

Para depurar essa violação em um computador com Windows 8.1, escolha B (Corrompido) e digite o comando do depurador sugerido (!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'.

O comando !deadlock 3 também pode ser usado para mostrar mais informações, incluindo a pilha no momento da última aquisição:

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 >>

O depurador sugere o uso do comando kb (Backtrace de pilha de exibição) para exibir o rastreamento de pilha atual.

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

A saída do depurador mostra que o driver em questão violou essa regra ao adquirir e segurar o Bloqueio A antes de adquirir o Bloqueio B em um thread, e agora adquiriu o Bloqueio B e está tentando adquirir o Bloqueio A em outro thread. O primeiro thread (Thread 0) é encerrado, então a aquisição e posterior liberação desses dois bloqueios aconteceu em algum momento desde que a imagem do driver foi carregada.

Quando símbolos apropriados para o driver de teste são carregados, o depurador mostra a função na qual o bloqueio foi adquirido no momento. Neste exemplo, acontece que tanto o Bloqueio A quanto o Bloqueio B são adquiridos na mesma função. No Thread 0, ambos são adquiridos no SystemControlIrpWorker. No Thread 1, ambos são adquiridos no DeviceControlIrpWorker (mostrado na saída Bloqueio B do !deadlock 3 e na saída da pilha atual (kb).

Neste ponto, uma revisão do código-fonte de cada função deve revelar que existe um caminho de código onde os bloqueios podem ser adquiridos em tal ordem.

MyTestDriver! AlphaLock e MyTestDriver! BravoLock são objetos disponíveis globalmente no driver:

include "MyTestDriverHeader.h"

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

Dentro da função SystemControlIrpWorker, existe um caminho onde AlphaLock (Bloqueio A na saída !deadlock) é adquirido e mantido quando BravoLock (Bloqueio B) é adquirido. Vale destacar também que os bloqueios são liberados corretamente na ordem inversa em que são adquiridos. (O código a seguir é fortemente editado para mostrar apenas os elementos necessários para gerar esse cenário).

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>>
}

Se você analisar o seguinte exemplo de função DeviceControlIrpWorker, verá que é possível adquirir os bloqueios na ordem inversa. Ou seja, o BravoLock pode ser adquirido e mantido ao tentar adquirir o AlphaLock. O exemplo a seguir é simplificado, mas mostra que há um caminho possível em que uma violação pode ocorrer.

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>>
}

Para corrigir essa possível violação, o correto seria garantir que, sempre que o driver tentar adquirir o AlphaLock, ele verifique e certifique-se de que o BravoLock não seja mantido. A correção mais simples pode ser simplesmente liberar o BravoLock e readquiri-lo assim que o AlphaLock for adquirido. Entretanto, mudanças de código mais significativas poderão ser necessárias se for vital que os dados que o BravoLock está protegendo não mudem enquanto se espera pelo AlphaLock e pela reaquisição do BravoLock.

Para obter mais informações sobre bloqueios de rotação e outras técnicas de sincronização, consulte Bloqueios de rotação.

Bloqueios de rotação

Verificação de bug 0xC4: DRIVER_VERIFIER_DETECTED_VIOLATION

!deadlock